{% extends "console/layout.html" %}
{% block title %}Settings · {{site_name|default('RustPBX')}}{% endblock %}
{% block content %}
<div class="p-6">
    <div class="mx-auto max-w-7xl space-y-6" x-data='settingsConsole({
        basePath: {{ (base_path | default("/console")) | tojson }},
        settings: {{ settings | default({}) | tojson }}
    })' x-init="init()">
        <div x-show="appReloadModal.open" x-cloak
            class="fixed inset-0 z-50 flex items-center justify-center bg-slate-900/60 px-4 py-6"
            @keydown.escape.window="closeReloadModal()" @click.self="closeReloadModal()">
            <div class="w-full max-w-3xl rounded-2xl bg-white shadow-2xl ring-1 ring-black/10">
                <div class="flex items-center justify-between border-b border-slate-200 px-6 py-4">
                    <div>
                        <h3 class="text-base font-semibold text-slate-900">Reload application</h3>
                        <p class="text-xs text-slate-500">Validate configuration and restart services safely.</p>
                    </div>
                    <button type="button" class="rounded-md p-2 text-slate-400 transition hover:text-slate-600"
                        @click="closeReloadModal()" :disabled="appReloading">
                        <span class="sr-only">Close</span>
                        <svg class="h-4 w-4" viewBox="0 0 20 20" fill="none" stroke="currentColor" stroke-width="1.6">
                            <path stroke-linecap="round" stroke-linejoin="round" d="M6 6l8 8M14 6l-8 8" />
                        </svg>
                    </button>
                </div>
                <div class="px-6 py-4">
                    <div class="rounded-lg border border-slate-200 bg-slate-50 px-4 py-3 text-xs text-slate-600">
                        <p>This action restarts SIP proxy, user agent, and console services. Active calls will drop and
                            new requests queue until the restart completes.</p>
                    </div>
                    <ul class="mt-4 space-y-3">
                        <template x-for="check in appReloadModal.checks" :key="check.id">
                            <li class="flex items-start gap-3">
                                <span class="mt-1 flex h-2.5 w-2.5 flex-none rounded-full"
                                    :class="reloadCheckIndicatorClass(check.status)"></span>
                                <div class="flex-1 text-sm">
                                    <div class="flex items-center gap-2">
                                        <div class="font-semibold text-slate-800" x-text="check.label"></div>
                                        <span class="text-[10px] font-semibold uppercase tracking-wide"
                                            :class="reloadCheckStatusClass(check.status)"
                                            x-text="reloadCheckStatusLabel(check.status)"></span>
                                    </div>
                                    <div class="text-xs text-slate-500" x-text="check.detail || check.hint"></div>
                                </div>
                            </li>
                        </template>
                    </ul>

                    <template x-if="appReloadModal.error">
                        <div class="mt-4 rounded-lg border border-rose-200 bg-rose-50 px-4 py-3 text-xs text-rose-700">
                            <div class="font-semibold"
                                x-text="appReloadModal.mode === 'check' ? 'Validation failed' : 'Reload failed'"></div>
                            <p class="mt-1" x-text="appReloadModal.error"></p>
                        </div>
                    </template>

                    <template x-if="appReloadModal.message && !appReloadModal.error">
                        <div
                            class="mt-4 rounded-lg border border-emerald-200 bg-emerald-50 px-4 py-3 text-xs text-emerald-700">
                            <div class="font-semibold" x-text="appReloadModal.mode === 'check'
                                    ? (appReloadModal.status === 'running' ? 'Validation in progress' : 'Validation result')
                                    : 'Reload in progress'"></div>
                            <p class="mt-1" x-text="appReloadModal.message"></p>
                        </div>
                    </template>
                </div>
                <div class="flex items-center justify-between gap-3 border-t border-slate-200 px-6 py-4">
                    <div class="text-xs text-slate-500">
                        <span class="font-semibold" x-text="reloadStatusLabel"></span>
                    </div>
                    <div class="flex items-center gap-2">
                        <button type="button"
                            class="inline-flex items-center justify-center rounded-lg border border-slate-200 px-4 py-2 text-sm font-semibold text-slate-600 transition hover:bg-slate-100 disabled:cursor-not-allowed disabled:opacity-60"
                            @click="closeReloadModal()" :disabled="appReloading && appReloadModal.status === 'running'">
                            Cancel
                        </button>
                        <button type="button"
                            class="inline-flex items-center justify-center gap-2 rounded-lg border border-sky-200 bg-white px-5 py-2 text-sm font-semibold text-sky-600 shadow-sm transition hover:border-sky-300 hover:bg-sky-50 focus:outline-none focus:ring-2 focus:ring-sky-200 focus:ring-offset-2 disabled:cursor-not-allowed disabled:opacity-60"
                            @click="startReloadCheck()" :disabled="appReloading || appReloadModal.status === 'running'">
                            <svg class="h-4 w-4" viewBox="0 0 20 20" fill="none" stroke="currentColor"
                                stroke-width="1.6">
                                <path stroke-linecap="round" stroke-linejoin="round" d="M5 10l3 3 7-7" />
                            </svg>
                            <span
                                x-text="appReloadModal.mode === 'check' && appReloadModal.status === 'error' ? 'Retry check' : 'Validate configuration'"></span>
                        </button>
                        <template x-if="appReloadModal.mode === 'check' || appReloadModal.status !== 'success'">
                            <button type="button"
                                class="inline-flex items-center justify-center gap-2 rounded-lg bg-rose-600 px-5 py-2 text-sm font-semibold text-white shadow-sm transition hover:bg-rose-500 focus:outline-none focus:ring-2 focus:ring-rose-400 focus:ring-offset-2 disabled:cursor-not-allowed disabled:opacity-60 whitespace-nowrap"
                                @click="startReloadApplication()"
                                :disabled="appReloading || appReloadModal.status === 'running'">
                                <svg class="h-4 w-4" viewBox="0 0 20 20" fill="none" stroke="currentColor"
                                    stroke-width="1.6">
                                    <path stroke-linecap="round" stroke-linejoin="round" d="M7 5l6 5-6 5" />
                                </svg>
                                <span x-text="reloadConfirmLabel"></span>
                            </button>
                        </template>
                    </div>
                </div>
            </div>
        </div>

        <div x-show="aclReloadModal.open" x-cloak
            class="fixed inset-0 z-50 flex items-center justify-center bg-slate-900/60 px-4 py-6"
            @keydown.escape.window="closeAclReloadModal()" @click.self="closeAclReloadModal()">
            <div class="w-full max-w-md rounded-2xl bg-white shadow-2xl ring-1 ring-black/10">
                <div class="flex items-center justify-between border-b border-slate-200 px-6 py-4">
                    <div>
                        <h3 class="text-base font-semibold text-slate-900">Reload ACL rules</h3>
                        <p class="text-xs text-slate-500">Apply updated allow/deny lists without restarting the proxy.
                        </p>
                    </div>
                    <button type="button" class="rounded-md p-2 text-slate-400 transition hover:text-slate-600"
                        @click="closeAclReloadModal()" :disabled="aclReloading">
                        <span class="sr-only">Close</span>
                        <svg class="h-4 w-4" viewBox="0 0 20 20" fill="none" stroke="currentColor" stroke-width="1.6">
                            <path stroke-linecap="round" stroke-linejoin="round" d="M6 6l8 8M14 6l-8 8" />
                        </svg>
                    </button>
                </div>
                <div class="px-6 py-4">
                    <div class="rounded-lg border border-slate-200 bg-slate-50 px-4 py-3 text-xs text-slate-600">
                        <p>Live reload updates call admission immediately. Existing calls continue, but new sessions
                            follow the refreshed rules.</p>
                    </div>
                    <ul class="mt-4 space-y-3 text-sm text-slate-600">
                        <li class="flex items-start gap-3">
                            <span class="mt-1 flex h-2.5 w-2.5 flex-none rounded-full"
                                :class="aclReloadModal.status === 'success' ? 'bg-emerald-500' : (aclReloadModal.status === 'running' ? 'bg-sky-400 animate-pulse' : 'bg-slate-300')"></span>
                            <div>
                                <div class="font-semibold text-slate-800">Refresh proxy filters</div>
                                <div class="text-xs text-slate-500">Send reload command to the SIP proxy.</div>
                            </div>
                        </li>
                        <li class="flex items-start gap-3">
                            <span class="mt-1 flex h-2.5 w-2.5 flex-none rounded-full"
                                :class="aclReloadModal.status === 'success' ? 'bg-emerald-500' : (aclReloadModal.status === 'running' ? 'bg-sky-400 animate-pulse' : 'bg-slate-300')"></span>
                            <div>
                                <div class="font-semibold text-slate-800">Confirm rule summary</div>
                                <div class="text-xs text-slate-500">Review new totals and metrics in the panel.</div>
                            </div>
                        </li>
                    </ul>

                    <template x-if="aclReloadModal.error">
                        <div class="mt-4 rounded-lg border border-rose-200 bg-rose-50 px-4 py-3 text-xs text-rose-700">
                            <div class="font-semibold">Reload failed</div>
                            <p class="mt-1" x-text="aclReloadModal.error"></p>
                        </div>
                    </template>

                    <template x-if="aclReloadModal.status === 'success' && aclReloadModal.message">
                        <div
                            class="mt-4 rounded-lg border border-emerald-200 bg-emerald-50 px-4 py-3 text-xs text-emerald-700">
                            <div class="font-semibold">Reload complete</div>
                            <p class="mt-1" x-text="aclReloadModal.message"></p>
                        </div>
                    </template>
                </div>
                <div class="flex items-center justify-between gap-3 border-t border-slate-200 px-6 py-4">
                    <div class="text-xs text-slate-500">
                        <span class="font-semibold" x-text="aclReloadStatusLabel"></span>
                    </div>
                    <div class="flex items-center gap-2">
                        <button type="button"
                            class="inline-flex items-center justify-center rounded-lg border border-slate-200 px-4 py-2 text-sm font-semibold text-slate-600 transition hover:bg-slate-100 disabled:cursor-not-allowed disabled:opacity-60"
                            @click="closeAclReloadModal()"
                            :disabled="aclReloading && aclReloadModal.status === 'running'">
                            Cancel
                        </button>
                        <template x-if="aclReloadModal.status !== 'success'">
                            <button type="button"
                                class="inline-flex items-center justify-center gap-2 rounded-lg bg-sky-600 px-4 py-2 text-sm font-semibold text-white shadow-sm transition hover:bg-sky-500 focus:outline-none focus:ring-2 focus:ring-sky-400 focus:ring-offset-2 disabled:cursor-not-allowed disabled:opacity-60"
                                @click="startAclReload()"
                                :disabled="aclReloading || aclReloadModal.status === 'running'">
                                <svg class="h-4 w-4" viewBox="0 0 20 20" fill="none" stroke="currentColor"
                                    stroke-width="1.6">
                                    <path stroke-linecap="round" stroke-linejoin="round" d="M7 5l6 5-6 5" />
                                </svg>
                                <span
                                    x-text="aclReloadModal.status === 'error' ? 'Retry reload' : 'Confirm reload'"></span>
                            </button>
                        </template>
                    </div>
                </div>
            </div>
        </div>

        <div class="flex flex-col gap-3 lg:flex-row lg:items-end lg:justify-between">
            <div>
                <p class="text-[11px] font-semibold uppercase tracking-wide text-sky-600">Control plane</p>
                <h1 class="text-2xl font-semibold text-slate-900">Settings</h1>
                <p class="text-sm text-slate-500">Configure platform behavior, storage, security, and team access.</p>
            </div>
            <div class="flex flex-wrap items-center gap-2">
                <template x-if="lastOperation">
                    <span class="rounded-full bg-slate-900/5 px-3 py-1.5 text-xs font-semibold text-slate-600">
                        Last action · <span x-text="lastOperation.timestamp"></span>
                    </span>
                </template>
                <span class="rounded-full bg-slate-900/5 px-3 py-1.5 text-xs font-semibold text-slate-600">
                    Uptime · <span class="font-mono text-slate-700" x-text="platform.uptime_pretty || '—'"></span>
                </span>
            </div>
        </div>

        <div class="rounded-xl bg-white p-3 shadow-sm ring-1 ring-black/5">
            <nav class="inline-flex w-full flex-wrap gap-2 rounded-lg border border-slate-200 bg-slate-50 p-1 text-xs font-semibold text-slate-600"
                role="tablist" aria-label="Settings sections">
                <template x-for="tab in tabs" :key="tab.id">
                    <button type="button" class="flex-1 rounded-md px-4 py-2 text-left transition sm:flex-none"
                        :class="activeTab === tab.id ? 'bg-white text-sky-700 shadow-sm' : 'text-slate-500 hover:text-slate-700'"
                        :aria-selected="activeTab === tab.id" :aria-controls="`settings-tab-${tab.id}`"
                        @click="activeTab = tab.id">
                        <div class="flex flex-col">
                            <span class="text-sm" x-text="tab.label"></span>
                            <template x-if="tab.description">
                                <span class="text-[11px] font-normal text-slate-400" x-text="tab.description"></span>
                            </template>
                        </div>
                    </button>
                </template>
            </nav>
        </div>

        <section x-show="activeTab === 'platform'" x-cloak x-transition.opacity class="space-y-6"
            id="settings-tab-platform" role="tabpanel" tabindex="0">
            <div class="grid gap-6 lg:grid-cols-2">
                <div class="rounded-xl bg-white p-6 shadow-sm ring-1 ring-black/5">
                    <div class="flex flex-col gap-2 md:flex-row md:items-center md:justify-between">
                        <div>
                            <h2 class="text-base font-semibold text-slate-900">Runtime overview</h2>
                            <p class="text-xs text-slate-500">Snapshot of core services and configuration provenance.
                            </p>
                        </div>
                        <div class="text-xs font-semibold text-slate-500">
                            Version · <span class="font-mono text-slate-700"
                                x-text="platform.version || site_version || '—'"></span>
                        </div>
                    </div>
                    <div class="mt-4 space-y-3 text-sm text-slate-600">
                        <div class="flex items-baseline justify-between">
                            <span class="text-xs uppercase tracking-wide text-slate-400">Address</span>
                            <span class="font-mono" x-text="proxyConfig.addr || '—'"></span>
                        </div>
                        <div class="flex items-baseline justify-between">
                            <span class="text-xs uppercase tracking-wide text-slate-400">Ports</span>
                            <span class="font-mono"
                                x-text="(proxyConfig.ports || []).map(port => `${port.label}:${port.value}`).join(', ') || '—'"></span>
                        </div>
                        <div class="flex items-baseline justify-between">
                            <span class="text-xs uppercase tracking-wide text-slate-400">Modules</span>
                            <span
                                x-text="(proxyConfig.modules || []).length ? proxyConfig.modules.join(', ') : '—'"></span>
                        </div>
                        <div class="flex items-baseline justify-between">
                            <span class="text-xs uppercase tracking-wide text-slate-400">Realms</span>
                            <span
                                x-text="(proxyConfig.realms || []).length ? proxyConfig.realms.join(', ') : '—'"></span>
                        </div>
                        <div class="flex items-baseline justify-between">
                            <span class="text-xs uppercase tracking-wide text-slate-400">Data sources</span>
                            <span
                                x-text="proxyConfig.data_sources ? `Routes · ${proxyConfig.data_sources.routes}, Trunks · ${proxyConfig.data_sources.trunks}` : '—'"></span>
                        </div>
                    </div>
                    <div class="mt-4 rounded-lg border border-slate-200 bg-slate-50 px-4 py-3">
                        <div class="text-[11px] font-semibold uppercase tracking-wide text-slate-400">Transactions
                        </div>
                        <div class="mt-3 grid grid-cols-3 gap-3 text-sm text-slate-700">
                            <div>
                                <div class="text-xs uppercase tracking-wide text-slate-400">Running</div>
                                <div class="mt-1 font-semibold text-slate-900"
                                    x-text="displayMetric(stats.proxy?.transactions?.running)"></div>
                            </div>
                            <div>
                                <div class="text-xs uppercase tracking-wide text-slate-400">Finished</div>
                                <div class="mt-1 font-semibold text-slate-900"
                                    x-text="displayMetric(stats.proxy?.transactions?.finished)"></div>
                            </div>
                            <div>
                                <div class="text-xs uppercase tracking-wide text-slate-400">Waiting ACK</div>
                                <div class="mt-1 font-semibold text-slate-900"
                                    x-text="displayMetric(stats.proxy?.transactions?.waiting_ack)"></div>
                            </div>
                        </div>
                        <div class="mt-3 border-t border-slate-200 pt-3 text-xs text-slate-500">
                            Active dialogs · <span class="font-semibold text-slate-700"
                                x-text="displayMetric(stats.proxy?.dialogs)"></span>
                        </div>
                    </div>
                </div>
                <div class="rounded-xl bg-white p-6 shadow-sm ring-1 ring-black/5">
                    <div class="flex flex-col gap-2 md:flex-row md:items-center md:justify-between">
                        <div>
                            <h2 class="text-base font-semibold text-slate-900">User agent runtime</h2>
                            <p class="text-xs text-slate-500">Registration service state and handshake metrics.</p>
                        </div>
                        <span class="rounded-full px-3 py-1 text-xs font-semibold"
                            :class="useragentConfig.enabled ? 'bg-emerald-50 text-emerald-600 ring-1 ring-emerald-100' : 'bg-slate-100 text-slate-600 ring-1 ring-slate-200'">
                            <span x-text="useragentConfig.enabled ? 'Online' : 'Offline'"></span>
                        </span>
                    </div>
                    <div class="mt-4 space-y-3 text-sm text-slate-600">
                        <div class="flex items-baseline justify-between">
                            <span class="text-xs uppercase tracking-wide text-slate-400">Address</span>
                            <span class="font-mono" x-text="useragentConfig.addr || '—'"></span>
                        </div>
                        <div class="flex items-baseline justify-between">
                            <span class="text-xs uppercase tracking-wide text-slate-400">UDP port</span>
                            <span class="font-mono" x-text="displayMetric(useragentConfig.udp_port)"></span>
                        </div>
                        <div class="flex items-baseline justify-between">
                            <span class="text-xs uppercase tracking-wide text-slate-400">User-Agent</span>
                            <span x-text="useragentConfig.useragent || '—'"></span>
                        </div>
                        <div class="flex items-baseline justify-between">
                            <span class="text-xs uppercase tracking-wide text-slate-400">Call-ID suffix</span>
                            <span class="font-mono" x-text="useragentConfig.callid_suffix || '—'"></span>
                        </div>
                    </div>
                    <div class="mt-4 rounded-lg border border-slate-200 bg-slate-50 px-4 py-3">
                        <div class="text-[11px] font-semibold uppercase tracking-wide text-slate-400">Transactions
                        </div>
                        <div class="mt-3 grid grid-cols-3 gap-3 text-sm text-slate-700">
                            <div>
                                <div class="text-xs uppercase tracking-wide text-slate-400">Running</div>
                                <div class="mt-1 font-semibold text-slate-900"
                                    x-text="displayMetric(stats.useragent?.transactions?.running)"></div>
                            </div>
                            <div>
                                <div class="text-xs uppercase tracking-wide text-slate-400">Finished</div>
                                <div class="mt-1 font-semibold text-slate-900"
                                    x-text="displayMetric(stats.useragent?.transactions?.finished)"></div>
                            </div>
                            <div>
                                <div class="text-xs uppercase tracking-wide text-slate-400">Waiting ACK</div>
                                <div class="mt-1 font-semibold text-slate-900"
                                    x-text="displayMetric(stats.useragent?.transactions?.waiting_ack)"></div>
                            </div>
                        </div>
                        <div class="mt-3 border-t border-slate-200 pt-3 text-xs text-slate-500">
                            Active dialogs · <span class="font-semibold text-slate-700"
                                x-text="displayMetric(stats.useragent?.dialogs)"></span>
                        </div>
                    </div>
                </div>
            </div>

            <div class="rounded-xl bg-white p-6 shadow-sm ring-1 ring-black/5">
                <div class="flex flex-col gap-2 md:flex-row md:items-center md:justify-between">
                    <div>
                        <h2 class="text-base font-semibold text-slate-900">Key configuration</h2>
                        <p class="text-xs text-slate-500">Selected settings from the active configuration file.</p>
                    </div>
                </div>
                <div class="mt-4 overflow-x-auto">
                    <table class="min-w-full divide-y divide-slate-200 text-sm">
                        <thead
                            class="bg-slate-50 text-left text-xs font-semibold uppercase tracking-wide text-slate-500">
                            <tr>
                                <th class="px-4 py-2">Key</th>
                                <th class="px-4 py-2">Value</th>
                            </tr>
                        </thead>
                        <tbody class="divide-y divide-slate-100">
                            <template x-if="!keyConfigRows().length">
                                <tr>
                                    <td class="px-4 py-3 text-xs text-slate-400" colspan="2">No configuration
                                        metadata available.</td>
                                </tr>
                            </template>
                            <template x-for="item in keyConfigRows()" :key="item.label">
                                <tr>
                                    <td class="px-4 py-3 text-xs font-semibold uppercase tracking-wide text-slate-400"
                                        x-text="item.label"></td>
                                    <td class="px-4 py-3 text-sm text-slate-700">
                                        <div class="font-mono whitespace-pre-line" x-text="item.value"></div>
                                        <div class="text-xs text-slate-400" x-show="item.hint" x-text="item.hint">
                                        </div>
                                    </td>
                                </tr>
                            </template>
                        </tbody>
                    </table>
                </div>
            </div>

        </section>

        <section x-show="activeTab === 'storage'" x-cloak x-transition.opacity class="space-y-6"
            id="settings-tab-storage" role="tabpanel" tabindex="0">
            <div class="grid gap-6 lg:grid-cols-[minmax(0,2fr)_minmax(0,1fr)]">
                <div class="space-y-6">
                    <div class="rounded-xl bg-white p-6 shadow-sm ring-1 ring-black/5">
                        <div class="flex flex-col gap-2 md:flex-row md:items-end md:justify-between">
                            <div>
                                <h2 class="text-base font-semibold text-slate-900">Storage destinations</h2>
                                <p class="text-xs text-slate-500">Choose between local disk and S3-compatible buckets
                                    for recordings and diagnostics.</p>
                            </div>
                            <span class="text-xs font-semibold uppercase tracking-wide text-slate-400">
                                Active profile · <span class="text-sky-600"
                                    x-text="selectedStorageProfileData?.label || storageModeLabel(server.storage?.mode)"></span>
                            </span>
                        </div>
                        <div class="mt-4 grid gap-4 md:grid-cols-2">
                            <template x-for="profile in storageProfiles" :key="profile.id">
                                <button type="button" @click="selectStorageProfile(profile.id)"
                                    class="group flex flex-col gap-2 rounded-lg border px-4 py-3 text-left transition"
                                    :class="selectedStorageProfile === profile.id ? 'border-sky-300 bg-sky-50 shadow-sm' : 'border-slate-200 bg-white hover:border-slate-300'">
                                    <div class="flex items-center justify-between">
                                        <span class="font-semibold text-slate-800" x-text="profile.label"></span>
                                        <span class="text-[11px] font-semibold uppercase tracking-wide"
                                            :class="selectedStorageProfile === profile.id ? 'text-sky-600' : 'text-slate-400'">
                                            <span
                                                x-text="selectedStorageProfile === profile.id ? 'Active' : 'Preview'"></span>
                                        </span>
                                    </div>
                                    <p class="text-xs text-slate-500" x-text="profile.description"></p>
                                    <dl class="mt-2 space-y-1 text-xs text-slate-500">
                                        <template x-for="item in profileConfig(profile)"
                                            :key="profile.id + '-' + item.key">
                                            <div class="flex items-center justify-between">
                                                <dt class="text-slate-400" x-text="item.label"></dt>
                                                <dd class="font-mono text-slate-600" x-text="item.value"></dd>
                                            </div>
                                        </template>
                                    </dl>
                                </button>
                            </template>
                        </div>
                        <div class="mt-4 rounded-lg border border-dashed border-slate-200 p-4 text-xs text-slate-500"
                            x-show="selectedStorageProfileData">
                            <div class="text-xs font-semibold uppercase tracking-wide text-slate-400">Profile notes
                            </div>
                            <p class="mt-2" x-text="selectedStorageProfileData?.description"></p>
                            <div class="mt-3 text-[11px] text-slate-400">
                                Selected profile ID · <span class="font-mono text-slate-600"
                                    x-text="selectedStorageProfileData?.id"></span>
                            </div>
                        </div>
                    </div>
                </div>
            </div>

        </section>

        <section x-show="activeTab === 'security'" x-cloak x-transition.opacity class="space-y-6"
            id="settings-tab-security" role="tabpanel" tabindex="0">
            <div class="grid gap-6 lg:grid-cols-[minmax(0,1.4fr)_minmax(0,1fr)]">
                <div class="space-y-6">
                    <div class="rounded-xl bg-white p-6 shadow-sm ring-1 ring-black/5">
                        <div class="flex flex-col gap-2 md:flex-row md:items-center md:justify-between">
                            <div>
                                <h2 class="text-base font-semibold text-slate-900">Access control lists</h2>
                                <p class="text-xs text-slate-500">Network admission rules from config files and embedded
                                    sets.</p>
                            </div>
                            <div class="text-right text-xs text-slate-500">
                                <div class="text-sm font-semibold text-slate-800"
                                    x-text="acl.active_rules.length ? acl.active_rules.length + ' active' : 'No rules'">
                                </div>
                                <div class="text-[11px] uppercase tracking-wide">Rule status</div>
                            </div>
                        </div>
                        <div class="mt-4 grid gap-3 sm:grid-cols-3">
                            <div class="rounded-lg border border-slate-200 px-4 py-3">
                                <div class="text-[11px] font-semibold uppercase tracking-wide text-slate-400">Embedded
                                    definitions</div>
                                <div class="mt-1 text-sm font-semibold text-slate-800" x-text="acl.embedded_count || 0">
                                </div>
                                <div class="text-xs text-slate-500">Rules baked into config.</div>
                            </div>
                            <div class="rounded-lg border border-slate-200 px-4 py-3">
                                <div class="text-[11px] font-semibold uppercase tracking-wide text-slate-400">File
                                    sources</div>
                                <div class="mt-1 text-sm font-semibold text-slate-800"
                                    x-text="acl.file_patterns.length ? acl.file_patterns.join(', ') : 'Inline only'">
                                </div>
                                <div class="text-xs text-slate-500">Glob patterns watched for ACL files.</div>
                            </div>
                            <div class="rounded-lg border border-slate-200 px-4 py-3">
                                <div class="text-[11px] font-semibold uppercase tracking-wide text-slate-400">Reload
                                    support</div>
                                <div class="mt-1 text-sm font-semibold"
                                    :class="acl.reload_supported ? 'text-emerald-600' : 'text-rose-600'"
                                    x-text="acl.reload_supported ? 'Available' : 'Proxy offline'"></div>
                                <div class="text-xs text-slate-500">Live reload requires the SIP proxy.</div>
                            </div>
                        </div>
                        <div class="mt-4 rounded-lg border border-slate-200 bg-slate-50">
                            <div
                                class="border-b border-slate-200 px-4 py-2 text-[11px] font-semibold uppercase tracking-wide text-slate-400">
                                Active rules snapshot
                            </div>
                            <pre class="max-h-48 overflow-auto px-4 py-3 text-xs text-slate-600"
                                x-text="formatAclRules(acl.active_rules)"></pre>
                        </div>
                        <template x-if="acl.metrics">
                            <div class="mt-4 rounded-lg border border-dashed border-slate-200 px-4 py-3">
                                <div class="text-[11px] font-semibold uppercase tracking-wide text-slate-400">Reload
                                    metrics</div>
                                <dl class="mt-3 grid gap-3 text-xs text-slate-600 sm:grid-cols-2 lg:grid-cols-4">
                                    <div>
                                        <dt class="text-[11px] uppercase tracking-wide text-slate-400">Total rules</dt>
                                        <dd class="mt-1 text-sm font-semibold text-slate-800"
                                            x-text="acl.metrics?.total ?? '—'"></dd>
                                    </div>
                                    <div>
                                        <dt class="text-[11px] uppercase tracking-wide text-slate-400">Embedded rules
                                        </dt>
                                        <dd class="mt-1 text-sm font-semibold text-slate-800"
                                            x-text="acl.metrics?.config_count ?? 0"></dd>
                                    </div>
                                    <div>
                                        <dt class="text-[11px] uppercase tracking-wide text-slate-400">File rules</dt>
                                        <dd class="mt-1 text-sm font-semibold text-slate-800"
                                            x-text="acl.metrics?.file_count ?? 0"></dd>
                                    </div>
                                    <div>
                                        <dt class="text-[11px] uppercase tracking-wide text-slate-400">Reload duration
                                        </dt>
                                        <dd class="mt-1 text-sm font-semibold text-slate-800"
                                            x-text="formatDuration(acl.metrics?.duration_ms) || '—'"></dd>
                                    </div>
                                </dl>
                                <template x-if="acl.metrics?.files?.length">
                                    <div class="mt-3 text-xs text-slate-600">
                                        <div class="text-[11px] uppercase tracking-wide text-slate-400">Loaded files
                                        </div>
                                        <ul class="mt-1 space-y-1">
                                            <template x-for="file in acl.metrics.files" :key="file">
                                                <li class="rounded bg-slate-100 px-2 py-1 font-mono text-[11px] text-slate-700"
                                                    x-text="file"></li>
                                            </template>
                                        </ul>
                                    </div>
                                </template>
                                <template x-if="acl.metrics?.patterns?.length">
                                    <div class="mt-3 text-xs text-slate-600">
                                        <div class="text-[11px] uppercase tracking-wide text-slate-400">File patterns
                                        </div>
                                        <ul class="mt-1 space-y-1">
                                            <template x-for="pattern in acl.metrics.patterns" :key="pattern">
                                                <li class="rounded bg-slate-100 px-2 py-1 font-mono text-[11px] text-slate-700"
                                                    x-text="pattern"></li>
                                            </template>
                                        </ul>
                                    </div>
                                </template>
                            </div>
                        </template>
                        <template x-if="operations.length">
                            <div class="mt-4 rounded-xl border border-rose-200 bg-rose-50 p-5">
                                <div class="flex items-center justify-between">
                                    <h3 class="text-sm font-semibold text-rose-700">Danger zone</h3>
                                </div>
                                <p class="mt-2 text-xs text-slate-500">Reload actions apply immediately to the SIP
                                    proxy in-memory state.</p>
                                <div class="mt-4 space-y-4 text-sm text-slate-600">
                                    <template x-for="action in operations" :key="action.id">
                                        <div class="rounded-lg border border-rose-200 bg-rose-50 p-4">
                                            <div
                                                class="flex flex-col gap-3 sm:flex-row sm:items-center sm:justify-between">
                                                <div class="space-y-2">
                                                    <div class="text-sm font-semibold text-slate-700"
                                                        x-text="action.label">
                                                    </div>
                                                    <p class="text-xs text-slate-600" x-show="action.description"
                                                        x-text="action.description"></p>
                                                </div>
                                                <button type="button"
                                                    class="inline-flex items-center justify-center gap-2 rounded-md border border-rose-500 bg-rose-600 px-4 py-2 text-xs font-semibold text-white shadow-sm transition hover:bg-rose-500 focus:outline-none focus:ring-2 focus:ring-rose-400 focus:ring-offset-2"
                                                    :disabled="operationDisabled(action)"
                                                    @click="triggerOperation(action)">
                                                    <svg class="h-4 w-4" viewBox="0 0 20 20" fill="none"
                                                        stroke="currentColor" stroke-width="1.6">
                                                        <path stroke-linecap="round" stroke-linejoin="round"
                                                            d="M7 5l6 5-6 5" />
                                                    </svg>
                                                    <span x-text="operationButtonLabel(action)"></span>
                                                </button>
                                            </div>
                                        </div>
                                    </template>
                                </div>
                                <template x-if="lastOperation">
                                    <div
                                        class="mt-4 rounded-lg border border-slate-200 bg-white px-3 py-2 text-xs text-slate-500">
                                        <div class="font-semibold text-slate-700" x-text="lastOperation.label"></div>
                                        <div x-text="lastOperation.timestamp"></div>
                                        <div class="mt-1" x-text="lastOperation.status"></div>
                                    </div>
                                </template>
                            </div>
                        </template>
                    </div>

                </div>
                <div class="space-y-6">
                    <div class="rounded-xl bg-white p-6 shadow-sm ring-1 ring-black/5">
                        <div class="flex items-center justify-between">
                            <h2 class="text-base font-semibold text-slate-900">Trusted sources</h2>
                            <span class="text-xs text-slate-400"
                                x-text="(security.trusted_ips || []).length ? (security.trusted_ips.length + ' entries') : 'No entries'"></span>
                        </div>
                        <div class="mt-3 space-y-2 text-xs text-slate-600">
                            <template x-for="(item, index) in (security.trusted_ips || [])" :key="item + '-' + index">
                                <div class="rounded-lg border border-slate-200 bg-slate-50 px-3 py-2">
                                    <div class="font-mono"
                                        x-text="typeof item === 'string' ? item : (item.cidr || item.address || JSON.stringify(item))">
                                    </div>
                                    <div class="mt-1 text-[11px] text-slate-400"
                                        x-text="item.label || item.note || 'Trusted IP / network'"></div>
                                </div>
                            </template>
                            <template x-if="!(security.trusted_ips || []).length">
                                <div
                                    class="rounded-lg border border-dashed border-slate-200 px-3 py-4 text-center text-xs text-slate-400">
                                    No trusted CIDRs configured.
                                </div>
                            </template>
                        </div>
                    </div>

                    <div class="rounded-xl bg-white p-6 shadow-sm ring-1 ring-black/5">
                        <div class="flex items-center justify-between">
                            <h2 class="text-base font-semibold text-slate-900">Blocked user agents</h2>
                            <span class="text-xs text-slate-400"
                                x-text="(security.blocked_user_agents || []).length ? (security.blocked_user_agents.length + ' entries') : 'No entries'"></span>
                        </div>
                        <div class="mt-3 space-y-2 text-xs text-slate-600">
                            <template x-for="(agent, index) in (security.blocked_user_agents || [])"
                                :key="agent + '-' + index">
                                <div class="rounded-lg border border-slate-200 bg-slate-50 px-3 py-2 font-mono">
                                    <span x-text="agent"></span>
                                </div>
                            </template>
                            <template x-if="!(security.blocked_user_agents || []).length">
                                <div
                                    class="rounded-lg border border-dashed border-slate-200 px-3 py-4 text-center text-xs text-slate-400">
                                    No user agents blocked.
                                </div>
                            </template>
                        </div>
                    </div>

                    <div class="rounded-xl bg-white p-6 shadow-sm ring-1 ring-black/5">
                        <div class="flex items-center justify-between">
                            <h2 class="text-base font-semibold text-slate-900">Threat feed</h2>
                            <span class="text-xs text-slate-400"
                                x-text="(security.threat_feed || []).length ? (security.threat_feed.length + ' entries') : 'No entries'"></span>
                        </div>
                        <div class="mt-3 space-y-2 text-xs text-slate-600">
                            <template x-for="(entry, index) in (security.threat_feed || [])"
                                :key="index + '-' + (entry?.id || '')">
                                <div class="rounded-lg border border-slate-200 bg-slate-50 px-3 py-2">
                                    <div class="font-semibold text-slate-800"
                                        x-text="entry.title || entry.id || 'Feed item'">
                                    </div>
                                    <div class="mt-1 text-[11px] text-slate-500"
                                        x-text="entry.description || entry.note || 'No description provided.'"></div>
                                    <div class="mt-1 text-[11px] text-slate-400" x-text="entry.source || ''"></div>
                                </div>
                            </template>
                            <template x-if="!(security.threat_feed || []).length">
                                <div
                                    class="rounded-lg border border-dashed border-slate-200 px-3 py-4 text-center text-xs text-slate-400">
                                    No active threats reported.
                                </div>
                            </template>
                        </div>
                    </div>
                </div>
            </div>
        </section>
        <section x-show="activeTab === 'asr'" x-cloak x-transition.opacity class="space-y-6" id="settings-tab-asr"
            role="tabpanel" tabindex="0">

            <div class="grid gap-6 lg:grid-cols-[minmax(0,2fr)_minmax(0,1fr)]">
                <div class="space-y-6">
                    <div class="rounded-xl bg-white p-6 shadow-sm ring-1 ring-black/5">
                        <div class="flex flex-col gap-3 lg:flex-row lg:items-center lg:justify-between">
                            <div>
                                <h2 class="text-base font-semibold text-slate-900">ASR providers</h2>
                                <p class="text-xs text-slate-500">Manage speech-to-text integrations and authentication
                                    tokens.
                                </p>
                            </div>
                            <div class="flex items-center gap-3 text-xs text-slate-500">
                                <span>Callback:</span>
                                <span class="rounded-lg bg-slate-50 px-3 py-1 font-mono"
                                    x-text="asr.callback?.url"></span>
                            </div>
                        </div>

                        <div class="mt-5 grid gap-6 lg:grid-cols-[minmax(0,1.4fr)_minmax(0,1fr)]">
                            <div class="space-y-4">
                                <label class="flex flex-col gap-2">
                                    <span class="text-xs font-semibold uppercase tracking-wide text-slate-400">Active
                                        provider</span>
                                    <select x-model="selectedProvider"
                                        class="rounded-lg border border-slate-200 px-3 py-2 text-sm text-slate-700 focus:border-sky-300 focus:outline-none focus:ring-2 focus:ring-sky-200">
                                        <template x-for="provider in asr.providers || []" :key="provider.id">
                                            <option :value="provider.id" x-text="provider.name"></option>
                                        </template>
                                    </select>
                                </label>
                                <div class="rounded-lg border border-slate-200 p-4 text-sm text-slate-600"
                                    x-show="activeProvider">
                                    <div class="flex items-center justify-between">
                                        <div class="text-base font-semibold text-slate-800"
                                            x-text="activeProvider ? activeProvider.name : '—'"></div>
                                        <span
                                            class="rounded-full bg-emerald-50 px-2 py-0.5 text-[11px] font-semibold text-emerald-600">Connected</span>
                                    </div>
                                    <div class="mt-2 text-xs text-slate-500">Token hint: <span class="font-mono"
                                            x-text="activeProvider ? activeProvider.token_hint : '—'"></span></div>
                                    <div class="mt-3 text-xs text-slate-500">Supported languages</div>
                                    <div class="mt-1 flex flex-wrap gap-2 text-[11px]">
                                        <template x-for="lang in asr.languages || []" :key="lang">
                                            <span
                                                class="rounded-full bg-slate-100 px-2 py-0.5 font-semibold text-slate-600"
                                                x-text="lang"></span>
                                        </template>
                                    </div>
                                    <div class="mt-4 flex flex-wrap items-center gap-2 text-xs text-slate-500">
                                        <span class="font-semibold text-slate-700">Callback auth:</span>
                                        <span class="rounded-lg bg-slate-50 px-2 py-0.5 font-mono text-xs"
                                            x-text="asr.callback?.auth_header"></span>
                                    </div>
                                </div>
                            </div>
                            <div class="space-y-4">
                                <div
                                    class="rounded-lg border border-dashed border-slate-200 p-4 text-xs text-slate-500">
                                    <h3 class="text-xs font-semibold uppercase tracking-wide text-slate-400">Run
                                        connectivity
                                        test</h3>
                                    <p class="mt-2">Send a short prompt to confirm that the ASR provider is reachable
                                        and the
                                        token has sufficient quota.</p>
                                    <button type="button" @click="runAsrTest"
                                        class="mt-4 inline-flex items-center gap-2 rounded-lg border border-slate-200 px-3 py-2 text-xs font-semibold text-slate-600 transition hover:border-emerald-300 hover:text-emerald-700">
                                        <svg class="h-4 w-4" viewBox="0 0 20 20" fill="none" stroke="currentColor"
                                            stroke-width="1.8">
                                            <path stroke-linecap="round" stroke-linejoin="round"
                                                d="M5 10h10M10 5l5 5-5 5" />
                                        </svg>
                                        Trigger test transcription
                                    </button>
                                    <template x-if="asrProbe">
                                        <div class="mt-4 rounded-lg border px-3 py-2"
                                            :class="asrProbe.success ? 'border-emerald-200 bg-emerald-50 text-emerald-700' : 'border-rose-200 bg-rose-50 text-rose-700'">
                                            <div class="flex items-center justify-between">
                                                <span class="font-semibold" x-text="asrProbe.message"></span>
                                                <span class="text-xs" x-text="asrProbe.timestamp"></span>
                                            </div>
                                            <p class="mt-1 text-xs" x-text="asrProbe.detail"></p>
                                        </div>
                                    </template>
                                </div>
                            </div>
                        </div>
                    </div>
                </div>
            </div>
        </section>

        <section x-show="activeTab === 'departments'" x-cloak x-transition.opacity class="space-y-6"
            id="settings-tab-departments" role="tabpanel" tabindex="0">
            <div class="grid gap-6 lg:grid-cols-[minmax(0,2fr)_minmax(0,1fr)]">
                <div class="space-y-6">
                    <div class="rounded-xl bg-white p-6 shadow-sm ring-1 ring-black/5">
                        <div class="flex flex-col gap-3 lg:flex-row lg:items-center lg:justify-between">
                            <div>
                                <h2 class="text-base font-semibold text-slate-900">Departments</h2>
                                <p class="text-xs text-slate-500">Manage contact metadata and escalation notes for each
                                    business unit.</p>
                            </div>
                            <div class="flex flex-wrap items-center gap-2 text-xs font-semibold text-slate-500">
                                <span class="rounded-full bg-slate-100 px-2 py-0.5 text-slate-600"
                                    x-text="departmentSummary"></span>
                                <button type="button"
                                    class="inline-flex items-center gap-2 rounded-lg border border-dashed border-slate-300 px-3 py-1.5 font-semibold text-slate-600 transition hover:border-sky-300 hover:text-sky-700"
                                    @click="openDepartmentCreate()">
                                    <svg class="h-3.5 w-3.5" viewBox="0 0 20 20" fill="none" stroke="currentColor"
                                        stroke-width="1.8">
                                        <path stroke-linecap="round" stroke-linejoin="round" d="M10 4v12m6-6H4" />
                                    </svg>
                                    New department
                                </button>
                            </div>
                        </div>
                        <div class="mt-4 space-y-4">
                            <div class="grid gap-3 md:grid-cols-[minmax(0,1fr)_auto]">
                                <label class="flex flex-col gap-1 text-sm font-medium text-slate-700">
                                    Search
                                    <input type="search" x-model="departmentParams.q"
                                        @input.debounce.400ms="applyDepartmentFilters()"
                                        class="rounded-lg border border-slate-200 px-3 py-2 text-sm text-slate-700 focus:border-sky-300 focus:outline-none focus:ring-2 focus:ring-sky-200"
                                        placeholder="Name, label, or slug">
                                </label>
                                <div class="flex items-end justify-end gap-2">
                                    <button type="button"
                                        class="inline-flex items-center gap-1 rounded-lg border border-slate-200 px-3 py-1.5 text-xs font-semibold text-slate-600 transition hover:border-slate-300 hover:text-slate-800"
                                        @click="resetDepartmentFilters()">
                                        Clear
                                    </button>
                                    <button type="button"
                                        class="inline-flex items-center gap-1 rounded-lg border border-slate-200 px-3 py-1.5 text-xs font-semibold text-slate-600 transition hover:border-sky-300 hover:text-sky-700"
                                        @click="fetchDepartments()">
                                        Refresh
                                    </button>
                                </div>
                            </div>
                            <template x-if="departmentError">
                                <div class="rounded-lg border border-rose-200 bg-rose-50 px-3 py-2 text-xs text-rose-700"
                                    x-text="departmentError"></div>
                            </template>
                            <div class="grid gap-4 lg:grid-cols-[minmax(0,0.9fr)_minmax(0,1.1fr)]">
                                <div class="space-y-2">
                                    <template x-if="departmentLoading">
                                        <div
                                            class="rounded-lg border border-dashed border-slate-200 px-3 py-6 text-center text-xs text-slate-400">
                                            Loading departments…
                                        </div>
                                    </template>
                                    <template x-if="!departmentLoading && !(directory.departments || []).length">
                                        <p
                                            class="rounded-lg border border-dashed border-slate-200 px-3 py-6 text-center text-xs text-slate-400">
                                            No departments yet. Create one to get started.
                                        </p>
                                    </template>
                                    <template x-if="!departmentLoading">
                                        <template x-for="dept in directory.departments || []" :key="dept.id">
                                            <button type="button" @click="selectDepartment(dept.id)"
                                                class="flex w-full items-center justify-between rounded-lg border px-3 py-2 text-left text-sm transition"
                                                :class="String(selectedDepartmentId) === String(dept.id) ? 'border-sky-300 bg-sky-50 text-sky-700 shadow-sm' : 'border-slate-200 bg-white text-slate-600 hover:border-slate-300'">
                                                <div>
                                                    <div class="font-semibold text-slate-800"
                                                        x-text="dept.display_label || dept.name"></div>
                                                    <div class="text-xs text-slate-400">
                                                        Slug · <span x-text="dept.slug || '—'"></span>
                                                    </div>
                                                </div>
                                                <div class="flex flex-col items-end gap-1 text-right">
                                                    <span
                                                        class="rounded-full bg-slate-100 px-2 py-0.5 text-[11px] font-semibold text-slate-600"
                                                        x-text="dept.manager_contact || 'No contact'"></span>
                                                    <span class="text-[11px] text-slate-400"
                                                        x-text="'Updated · ' + formatDateTime(dept.updated_at)"></span>
                                                </div>
                                            </button>
                                        </template>
                                    </template>
                                </div>
                                <div class="space-y-4">
                                    <div class="rounded-lg border border-slate-200 p-4 text-sm text-slate-600"
                                        x-show="selectedDepartment">
                                        <template x-if="selectedDepartment">
                                            <div class="space-y-4">
                                                <div class="flex items-start justify-between gap-3">
                                                    <div>
                                                        <div class="text-xs uppercase tracking-wide text-slate-400">
                                                            Department</div>
                                                        <div class="mt-1 text-base font-semibold text-slate-900"
                                                            x-text="selectedDepartment.display_label || selectedDepartment.name">
                                                        </div>
                                                        <div class="text-xs text-slate-500">
                                                            Name · <span x-text="selectedDepartment.name"></span>
                                                        </div>
                                                    </div>
                                                    <div class="flex flex-col items-end gap-2 text-xs">
                                                        <span
                                                            class="rounded-full bg-slate-100 px-2 py-0.5 font-semibold text-slate-600"
                                                            x-text="selectedDepartment.color || 'No color'"></span>
                                                        <div class="flex items-center gap-2">
                                                            <button type="button"
                                                                class="inline-flex items-center gap-1 rounded-md border border-slate-200 px-2 py-1 font-semibold text-slate-600 transition hover:border-slate-300 hover:text-slate-800"
                                                                @click="openDepartmentEdit(selectedDepartment.id)">
                                                                <svg class="h-3.5 w-3.5" viewBox="0 0 20 20" fill="none"
                                                                    stroke="currentColor" stroke-width="1.6">
                                                                    <path stroke-linecap="round" stroke-linejoin="round"
                                                                        d="M4 13v3h3l9-9-3-3-9 9z" />
                                                                </svg>
                                                                Edit
                                                            </button>
                                                            <button type="button"
                                                                class="inline-flex items-center gap-1 rounded-md border border-rose-200 bg-rose-50 px-2 py-1 font-semibold text-rose-600 transition hover:border-rose-300 hover:bg-rose-100"
                                                                @click="deleteDepartment(selectedDepartment.id)">
                                                                <svg class="h-3.5 w-3.5" viewBox="0 0 20 20" fill="none"
                                                                    stroke="currentColor" stroke-width="1.6">
                                                                    <path stroke-linecap="round" stroke-linejoin="round"
                                                                        d="M6 6l8 8M6 14l8-8" />
                                                                </svg>
                                                                Remove
                                                            </button>
                                                        </div>
                                                    </div>
                                                </div>
                                                <div class="grid gap-3 text-xs text-slate-500">
                                                    <div class="flex items-center justify-between">
                                                        <span>Slug</span>
                                                        <span class="font-semibold text-slate-700"
                                                            x-text="selectedDepartment.slug || '—'"></span>
                                                    </div>
                                                    <div class="flex items-center justify-between">
                                                        <span>Manager contact</span>
                                                        <span class="font-semibold text-slate-700"
                                                            x-text="selectedDepartment.manager_contact || 'Not set'"></span>
                                                    </div>
                                                    <div class="flex items-center justify-between">
                                                        <span>Created</span>
                                                        <span class="font-semibold text-slate-700"
                                                            x-text="formatDateTime(selectedDepartment.created_at)"></span>
                                                    </div>
                                                    <div class="flex items-center justify-between">
                                                        <span>Updated</span>
                                                        <span class="font-semibold text-slate-700"
                                                            x-text="formatDateTime(selectedDepartment.updated_at)"></span>
                                                    </div>
                                                </div>
                                                <div>
                                                    <div class="text-xs uppercase tracking-wide text-slate-400">
                                                        Description</div>
                                                    <p class="mt-1 text-xs text-slate-500"
                                                        x-text="selectedDepartment.description || 'No description provided.'">
                                                    </p>
                                                </div>
                                                <div>
                                                    <div class="text-xs uppercase tracking-wide text-slate-400">Metadata
                                                    </div>
                                                    <pre class="mt-1 max-h-40 overflow-auto rounded-lg border border-slate-200 bg-slate-50 px-3 py-2 text-xs text-slate-600"
                                                        x-text="formatMetadata(selectedDepartment.metadata)"></pre>
                                                </div>
                                            </div>
                                        </template>
                                    </div>
                                    <template x-if="departmentFormVisible">
                                        <div
                                            class="rounded-xl border border-dashed border-slate-300 bg-slate-50/40 p-5 text-sm text-slate-600">
                                            <form class="space-y-4" @submit.prevent="saveDepartment">
                                                <header class="flex items-center justify-between">
                                                    <div>
                                                        <div class="text-xs uppercase tracking-wide text-slate-400"
                                                            x-text="departmentFormMode === 'edit' ? 'Update department' : 'Create department'">
                                                        </div>
                                                        <h3 class="text-base font-semibold text-slate-900"
                                                            x-text="departmentFormMode === 'edit' ? 'Edit department' : 'New department'">
                                                        </h3>
                                                    </div>
                                                    <button type="button"
                                                        class="text-xs font-semibold text-slate-400 transition hover:text-slate-600"
                                                        @click="cancelDepartmentForm()">Cancel</button>
                                                </header>
                                                <div class="grid gap-4 md:grid-cols-2">
                                                    <label
                                                        class="flex flex-col gap-1 text-sm font-medium text-slate-700">
                                                        Name
                                                        <input type="text" x-model="departmentDraft.name" required
                                                            class="rounded-lg border border-slate-200 px-3 py-2 text-sm text-slate-700 focus:border-sky-300 focus:outline-none focus:ring-2 focus:ring-sky-200"
                                                            placeholder="e.g. Customer success">
                                                    </label>
                                                    <label
                                                        class="flex flex-col gap-1 text-sm font-medium text-slate-700">
                                                        Display label
                                                        <input type="text" x-model="departmentDraft.display_label"
                                                            class="rounded-lg border border-slate-200 px-3 py-2 text-sm text-slate-700 focus:border-sky-300 focus:outline-none focus:ring-2 focus:ring-sky-200"
                                                            placeholder="Optional friendly name">
                                                    </label>
                                                    <label
                                                        class="flex flex-col gap-1 text-sm font-medium text-slate-700">
                                                        Slug
                                                        <input type="text" x-model="departmentDraft.slug"
                                                            class="rounded-lg border border-slate-200 px-3 py-2 text-sm text-slate-700 focus:border-sky-300 focus:outline-none focus:ring-2 focus:ring-sky-200"
                                                            placeholder="auto-generated if left blank">
                                                    </label>
                                                    <label
                                                        class="flex flex-col gap-1 text-sm font-medium text-slate-700">
                                                        Color
                                                        <input type="text" x-model="departmentDraft.color"
                                                            class="rounded-lg border border-slate-200 px-3 py-2 text-sm text-slate-700 focus:border-sky-300 focus:outline-none focus:ring-2 focus:ring-sky-200"
                                                            placeholder="#0EA5E9">
                                                    </label>
                                                    <label
                                                        class="flex flex-col gap-1 text-sm font-medium text-slate-700 md:col-span-2">
                                                        Manager contact
                                                        <input type="text" x-model="departmentDraft.manager_contact"
                                                            class="rounded-lg border border-slate-200 px-3 py-2 text-sm text-slate-700 focus:border-sky-300 focus:outline-none focus:ring-2 focus:ring-sky-200"
                                                            placeholder="Name or email">
                                                    </label>
                                                </div>
                                                <label class="flex flex-col gap-1 text-sm font-medium text-slate-700">
                                                    Description
                                                    <textarea rows="3" x-model="departmentDraft.description"
                                                        class="rounded-lg border border-slate-200 px-3 py-2 text-sm text-slate-700 focus:border-sky-300 focus:outline-none focus:ring-2 focus:ring-sky-200"
                                                        placeholder="Purpose, responsibilities, or escalation details."></textarea>
                                                </label>
                                                <label class="flex flex-col gap-1 text-sm font-medium text-slate-700">
                                                    Metadata (JSON)
                                                    <textarea rows="4" x-model="departmentDraft.metadataInput"
                                                        class="rounded-lg border border-slate-200 px-3 py-2 font-mono text-xs text-slate-700 focus:border-sky-300 focus:outline-none focus:ring-2 focus:ring-sky-200"
                                                        placeholder='{"timezone": "UTC"}'></textarea>
                                                    <span class="text-xs font-normal text-slate-400">Optional key/value
                                                        data stored with the department.</span>
                                                </label>
                                                <div class="flex items-center justify-end gap-3">
                                                    <button type="button"
                                                        class="inline-flex items-center justify-center rounded-lg border border-slate-200 px-4 py-2 text-sm font-semibold text-slate-600 transition hover:bg-slate-100"
                                                        @click="cancelDepartmentForm()">Cancel</button>
                                                    <button type="submit"
                                                        class="inline-flex items-center justify-center gap-2 rounded-lg bg-sky-600 px-4 py-2 text-sm font-semibold text-white shadow-sm transition hover:bg-sky-500 focus:outline-none focus:ring-2 focus:ring-sky-400 focus:ring-offset-2"
                                                        :disabled="departmentProcessing.save">
                                                        <svg class="h-4 w-4" viewBox="0 0 20 20" fill="none"
                                                            stroke="currentColor" stroke-width="1.8">
                                                            <path stroke-linecap="round" stroke-linejoin="round"
                                                                d="M5 10l3 3 7-7" />
                                                        </svg>
                                                        <span
                                                            x-text="departmentProcessing.save ? 'Saving…' : 'Save department'"></span>
                                                    </button>
                                                </div>
                                            </form>
                                        </div>
                                    </template>
                                </div>
                            </div>
                        </div>
                    </div>
                </div>
            </div>
        </section>

        <section x-show="activeTab === 'users'" x-cloak x-transition.opacity class="space-y-6" id="settings-tab-users"
            role="tabpanel" tabindex="0">
            <div class="grid gap-6 lg:grid-cols-[minmax(0,2fr)_minmax(0,1fr)]">
                <div class="space-y-6">
                    <div class="rounded-xl bg-white p-6 shadow-sm ring-1 ring-black/5">
                        <div class="flex flex-col gap-3 lg:flex-row lg:items-center lg:justify-between">
                            <div>
                                <h2 class="text-base font-semibold text-slate-900">Users</h2>
                                <p class="text-xs text-slate-500">Provision console access and enforce privileged
                                    account policies.</p>
                            </div>
                            <div class="flex flex-wrap items-center gap-2 text-xs font-semibold text-slate-500">
                                <span class="rounded-full bg-slate-100 px-2 py-0.5 text-slate-600"
                                    x-text="userSummary"></span>
                                <button type="button"
                                    class="inline-flex items-center gap-2 rounded-lg border border-dashed border-slate-300 px-3 py-1.5 font-semibold text-slate-600 transition hover:border-sky-300 hover:text-sky-700"
                                    @click="openUserCreate()">
                                    <svg class="h-3.5 w-3.5" viewBox="0 0 20 20" fill="none" stroke="currentColor"
                                        stroke-width="1.8">
                                        <path stroke-linecap="round" stroke-linejoin="round" d="M10 4v12m6-6H4" />
                                    </svg>
                                    Add team member
                                </button>
                            </div>
                        </div>
                        <div class="mt-4 space-y-4">
                            <div class="grid gap-3 md:grid-cols-[minmax(0,1fr)_auto]">
                                <label class="flex flex-col gap-1 text-sm font-medium text-slate-700">
                                    Search
                                    <input type="search" x-model="userParams.q"
                                        @input.debounce.400ms="applyUserFilters()"
                                        class="rounded-lg border border-slate-200 px-3 py-2 text-sm text-slate-700 focus:border-sky-300 focus:outline-none focus:ring-2 focus:ring-sky-200"
                                        placeholder="Email or username">
                                </label>
                                <div
                                    class="flex flex-col items-end gap-2 text-xs font-semibold text-slate-500 md:flex-row md:items-center">
                                    <label
                                        class="inline-flex items-center gap-2 rounded-lg border border-slate-200 px-3 py-1.5 text-xs text-slate-600 transition hover:border-slate-300 hover:text-slate-800">
                                        <input type="checkbox"
                                            class="h-4 w-4 rounded border-slate-300 text-sky-600 focus:ring-sky-400"
                                            x-model="userParams.activeOnly" @change="applyUserFilters()">
                                        Active only
                                    </label>
                                    <div class="flex items-center gap-2">
                                        <button type="button"
                                            class="inline-flex items-center gap-1 rounded-lg border border-slate-200 px-3 py-1.5 text-xs font-semibold text-slate-600 transition hover:border-slate-300 hover:text-slate-800"
                                            @click="resetUserFilters()">
                                            Clear
                                        </button>
                                        <button type="button"
                                            class="inline-flex items-center gap-1 rounded-lg border border-slate-200 px-3 py-1.5 text-xs font-semibold text-slate-600 transition hover:border-sky-300 hover:text-sky-700"
                                            @click="fetchUsers()">
                                            Refresh
                                        </button>
                                    </div>
                                </div>
                            </div>
                            <template x-if="userError">
                                <div class="rounded-lg border border-rose-200 bg-rose-50 px-3 py-2 text-xs text-rose-700"
                                    x-text="userError"></div>
                            </template>
                            <div class="grid gap-4 lg:grid-cols-[minmax(0,1fr)_minmax(0,1fr)]">
                                <div class="space-y-2">
                                    <template x-if="userLoading">
                                        <div
                                            class="rounded-lg border border-dashed border-slate-200 px-3 py-6 text-center text-xs text-slate-400">
                                            Loading users…
                                        </div>
                                    </template>
                                    <template x-if="!userLoading && !(directory.users || []).length">
                                        <p
                                            class="rounded-lg border border-dashed border-slate-200 px-3 py-6 text-center text-xs text-slate-400">
                                            No users match the current filters.
                                        </p>
                                    </template>
                                    <template x-if="!userLoading">
                                        <template x-for="user in directory.users || []" :key="user.id">
                                            <button type="button" @click="selectUser(user.id)"
                                                class="flex w-full items-center justify-between rounded-lg border px-3 py-2 text-left text-sm transition"
                                                :class="String(selectedUserId) === String(user.id) ? 'border-sky-300 bg-sky-50 text-sky-700 shadow-sm' : 'border-slate-200 bg-white text-slate-600 hover:border-slate-300'">
                                                <div>
                                                    <div class="font-semibold text-slate-800" x-text="user.username">
                                                    </div>
                                                    <div class="text-xs text-slate-400" x-text="user.email"></div>
                                                </div>
                                                <div class="flex flex-col items-end gap-1 text-right">
                                                    <span class="rounded-full px-2 py-0.5 text-[11px] font-semibold"
                                                        :class="userStatusClasses(user)"
                                                        x-text="userStatusLabel(user)"></span>
                                                    <span class="text-[11px] text-slate-400"
                                                        x-text="lastLoginLabel(user)"></span>
                                                </div>
                                            </button>
                                        </template>
                                    </template>
                                </div>
                                <div class="space-y-4">
                                    <div class="rounded-lg border border-slate-200 p-4 text-sm text-slate-600"
                                        x-show="selectedUser">
                                        <template x-if="selectedUser">
                                            <div class="space-y-4">
                                                <div class="flex items-start justify-between gap-3">
                                                    <div>
                                                        <div class="text-xs uppercase tracking-wide text-slate-400">User
                                                        </div>
                                                        <div class="mt-1 text-base font-semibold text-slate-900"
                                                            x-text="selectedUser.username"></div>
                                                        <div class="text-xs text-slate-500" x-text="selectedUser.email">
                                                        </div>
                                                    </div>
                                                    <div class="flex flex-col items-end gap-2 text-xs">
                                                        <span class="rounded-full px-2 py-0.5 text-[11px] font-semibold"
                                                            :class="userStatusClasses(selectedUser)"
                                                            x-text="userStatusLabel(selectedUser)"></span>
                                                        <div class="flex items-center gap-2">
                                                            <button type="button"
                                                                class="inline-flex items-center gap-1 rounded-md border border-slate-200 px-2 py-1 font-semibold text-slate-600 transition hover:border-slate-300 hover:text-slate-800"
                                                                @click="openUserEdit(selectedUser.id)">
                                                                <svg class="h-3.5 w-3.5" viewBox="0 0 20 20" fill="none"
                                                                    stroke="currentColor" stroke-width="1.6">
                                                                    <path stroke-linecap="round" stroke-linejoin="round"
                                                                        d="M4 13v3h3l9-9-3-3-9 9z" />
                                                                </svg>
                                                                Edit
                                                            </button>
                                                            <button type="button"
                                                                class="inline-flex items-center gap-1 rounded-md border border-rose-200 bg-rose-50 px-2 py-1 font-semibold text-rose-600 transition hover:border-rose-300 hover:bg-rose-100"
                                                                @click="deleteUser(selectedUser.id)">
                                                                <svg class="h-3.5 w-3.5" viewBox="0 0 20 20" fill="none"
                                                                    stroke="currentColor" stroke-width="1.6">
                                                                    <path stroke-linecap="round" stroke-linejoin="round"
                                                                        d="M6 6l8 8M6 14l8-8" />
                                                                </svg>
                                                                Remove
                                                            </button>
                                                        </div>
                                                    </div>
                                                </div>
                                                <div class="grid gap-3 text-xs text-slate-500">
                                                    <div class="flex items-center justify-between">
                                                        <span>Last login</span>
                                                        <span class="font-semibold text-slate-700"
                                                            x-text="selectedUser.last_login_at ? formatDateTime(selectedUser.last_login_at) : '—'"></span>
                                                    </div>
                                                    <div class="flex items-center justify-between">
                                                        <span>Last IP</span>
                                                        <span class="font-semibold text-slate-700"
                                                            x-text="selectedUser.last_login_ip || '—'"></span>
                                                    </div>
                                                    <div class="flex items-center justify-between">
                                                        <span>Created</span>
                                                        <span class="font-semibold text-slate-700"
                                                            x-text="formatDateTime(selectedUser.created_at)"></span>
                                                    </div>
                                                    <div class="flex items-center justify-between">
                                                        <span>Updated</span>
                                                        <span class="font-semibold text-slate-700"
                                                            x-text="formatDateTime(selectedUser.updated_at)"></span>
                                                    </div>
                                                </div>
                                                <div class="flex flex-wrap gap-2 text-[11px]">
                                                    <span class="rounded-full px-2 py-0.5 font-semibold"
                                                        :class="selectedUser.is_active ? 'bg-emerald-50 text-emerald-600 ring-1 ring-emerald-100' : 'bg-amber-50 text-amber-600 ring-1 ring-amber-100'"
                                                        x-text="selectedUser.is_active ? 'Login enabled' : 'Login disabled'"></span>
                                                    <span class="rounded-full px-2 py-0.5 font-semibold"
                                                        :class="selectedUser.is_staff ? 'bg-sky-50 text-sky-600 ring-1 ring-sky-100' : 'bg-slate-100 text-slate-600 ring-1 ring-slate-200'"
                                                        x-text="selectedUser.is_staff ? 'Staff privileges' : 'Standard user'"></span>
                                                    <span class="rounded-full px-2 py-0.5 font-semibold"
                                                        :class="selectedUser.is_superuser ? 'bg-purple-50 text-purple-600 ring-1 ring-purple-100' : 'bg-slate-100 text-slate-600 ring-1 ring-slate-200'"
                                                        x-text="selectedUser.is_superuser ? 'Superuser' : 'Limited'"></span>
                                                </div>
                                            </div>
                                        </template>
                                    </div>
                                    <template x-if="userFormVisible">
                                        <div
                                            class="rounded-xl border border-dashed border-slate-300 bg-slate-50/40 p-5 text-sm text-slate-600">
                                            <form class="space-y-4" @submit.prevent="saveUser">
                                                <header class="flex items-center justify-between">
                                                    <div>
                                                        <div class="text-xs uppercase tracking-wide text-slate-400"
                                                            x-text="userFormMode === 'edit' ? 'Update teammate' : 'Invite teammate'">
                                                        </div>
                                                        <h3 class="text-base font-semibold text-slate-900"
                                                            x-text="userFormMode === 'edit' ? 'Edit user' : 'New user'">
                                                        </h3>
                                                    </div>
                                                    <button type="button"
                                                        class="text-xs font-semibold text-slate-400 transition hover:text-slate-600"
                                                        @click="cancelUserForm()">Cancel</button>
                                                </header>
                                                <div class="grid gap-4 md:grid-cols-2">
                                                    <label
                                                        class="flex flex-col gap-1 text-sm font-medium text-slate-700">
                                                        Email
                                                        <input type="email" x-model="userDraft.email" required
                                                            class="rounded-lg border border-slate-200 px-3 py-2 text-sm text-slate-700 focus:border-sky-300 focus:outline-none focus:ring-2 focus:ring-sky-200"
                                                            placeholder="name@example.com">
                                                    </label>
                                                    <label
                                                        class="flex flex-col gap-1 text-sm font-medium text-slate-700">
                                                        Username
                                                        <input type="text" x-model="userDraft.username" required
                                                            class="rounded-lg border border-slate-200 px-3 py-2 text-sm text-slate-700 focus:border-sky-300 focus:outline-none focus:ring-2 focus:ring-sky-200"
                                                            placeholder="short-handle">
                                                    </label>
                                                </div>
                                                <label class="flex flex-col gap-1 text-sm font-medium text-slate-700">
                                                    Password
                                                    <input type="password" x-model="userDraft.password"
                                                        class="rounded-lg border border-slate-200 px-3 py-2 text-sm text-slate-700 focus:border-sky-300 focus:outline-none focus:ring-2 focus:ring-sky-200"
                                                        :placeholder="userFormMode === 'edit' ? 'Leave blank to keep current password' : 'Set an initial password'">
                                                    <span class="text-xs font-normal text-slate-400"
                                                        x-text="userFormMode === 'edit' ? 'Only fill this field to rotate the password.' : 'Password is required for new users.'"></span>
                                                </label>
                                                <div class="grid gap-3 md:grid-cols-3">
                                                    <label
                                                        class="flex items-center gap-2 text-xs font-semibold text-slate-600">
                                                        <input type="checkbox"
                                                            class="h-4 w-4 rounded border-slate-300 text-sky-600 focus:ring-sky-400"
                                                            x-model="userDraft.is_active">
                                                        Active
                                                    </label>
                                                    <label
                                                        class="flex items-center gap-2 text-xs font-semibold text-slate-600">
                                                        <input type="checkbox"
                                                            class="h-4 w-4 rounded border-slate-300 text-sky-600 focus:ring-sky-400"
                                                            x-model="userDraft.is_staff">
                                                        Staff access
                                                    </label>
                                                    <label
                                                        class="flex items-center gap-2 text-xs font-semibold text-slate-600">
                                                        <input type="checkbox"
                                                            class="h-4 w-4 rounded border-slate-300 text-sky-600 focus:ring-sky-400"
                                                            x-model="userDraft.is_superuser">
                                                        Superuser
                                                    </label>
                                                </div>
                                                <div class="flex items-center justify-end gap-3">
                                                    <button type="button"
                                                        class="inline-flex items-center justify-center rounded-lg border border-slate-200 px-4 py-2 text-sm font-semibold text-slate-600 transition hover:bg-slate-100"
                                                        @click="cancelUserForm()">Cancel</button>
                                                    <button type="submit"
                                                        class="inline-flex items-center justify-center gap-2 rounded-lg bg-sky-600 px-4 py-2 text-sm font-semibold text-white shadow-sm transition hover:bg-sky-500 focus:outline-none focus:ring-2 focus:ring-sky-400 focus:ring-offset-2"
                                                        :disabled="userProcessing.save">
                                                        <svg class="h-4 w-4" viewBox="0 0 20 20" fill="none"
                                                            stroke="currentColor" stroke-width="1.8">
                                                            <path stroke-linecap="round" stroke-linejoin="round"
                                                                d="M5 10l3 3 7-7" />
                                                        </svg>
                                                        <span
                                                            x-text="userProcessing.save ? 'Saving…' : 'Save user'"></span>
                                                    </button>
                                                </div>
                                            </form>
                                        </div>
                                    </template>
                                </div>
                            </div>
                        </div>
                    </div>
                </div>
            </div>
        </section>
    </div>
</div>

<script>
    document.addEventListener('alpine:init', () => {
        Alpine.data('settingsConsole', (options = {}) => ({
            basePath: options?.basePath || '/console',
            rawSettings: options?.settings || {},
            amiEndpoint: options?.amiEndpoint || '/ami/v1',
            tabs: [
                { id: 'platform', label: 'Platform overview' },
                { id: 'storage', label: 'Storage & retention' },
                { id: 'security', label: 'Security posture' },
                { id: 'asr', label: 'ASR integrations' },
                { id: 'departments', label: 'Departments' },
                { id: 'users', label: 'Users' },
            ],
            activeTab: 'platform',
            platform: {},
            proxyConfig: {},
            useragentConfig: {},
            stats: {},
            configMeta: { key_items: [] },
            acl: { active_rules: [], embedded_count: 0, file_patterns: [], reload_supported: false, metrics: null },
            operations: [],
            aclReloading: false,
            aclReloadModal: {
                open: false,
                status: 'idle',
                error: null,
                message: '',
                action: null,
            },
            appReloading: false,
            appReloadModal: {
                open: false,
                status: 'idle',
                error: null,
                message: '',
                action: null,
                mode: 'reload',
                checks: [],
            },
            server: {},
            retention: {},
            security: {},
            asr: {},
            directory: { departments: [], users: [], pending_invites: [] },
            selectedProvider: '',
            selectedStorageProfile: '',
            selectedDepartmentId: '',
            selectedUserId: '',
            lastOperation: null,
            asrProbe: null,
            departmentLoading: false,
            departmentError: null,
            departmentPagination: null,
            departmentParams: { page: 1, perPage: 20, q: '' },
            departmentProcessing: { save: false, delete: null },
            departmentFormVisible: false,
            departmentFormMode: 'create',
            departmentEditingId: '',
            departmentDraft: {
                name: '',
                display_label: '',
                slug: '',
                color: '',
                manager_contact: '',
                description: '',
                metadataInput: '',
            },
            userLoading: false,
            userError: null,
            userPagination: null,
            userParams: { page: 1, perPage: 20, q: '', activeOnly: false },
            userProcessing: { save: false, delete: null },
            userFormVisible: false,
            userFormMode: 'create',
            userEditingId: '',
            userDraft: {
                email: '',
                username: '',
                password: '',
                is_active: true,
                is_staff: false,
                is_superuser: false,
            },
            init() {
                const data = typeof this.rawSettings === 'string'
                    ? JSON.parse(this.rawSettings || '{}')
                    : (this.rawSettings || {});
                this.amiEndpoint = options?.amiEndpoint || data.ami_endpoint || '/ami/v1';
                this.platform = data.platform || {};
                this.proxyConfig = data.proxy || {};
                if (!Array.isArray(this.proxyConfig.realms)) {
                    if (typeof this.proxyConfig.realms === 'string' && this.proxyConfig.realms.length) {
                        this.proxyConfig.realms = [this.proxyConfig.realms];
                    } else {
                        this.proxyConfig.realms = [];
                    }
                }
                this.useragentConfig = data.useragent || {};
                this.stats = data.stats || {};
                this.configMeta = data.config || { key_items: [] };
                if (!Array.isArray(this.configMeta.key_items)) {
                    this.configMeta.key_items = [];
                }
                this.acl = data.acl || { active_rules: [], embedded_count: 0, file_patterns: [], reload_supported: false, metrics: null };
                if (!Array.isArray(this.acl.active_rules)) {
                    this.acl.active_rules = [];
                }
                if (!Array.isArray(this.acl.file_patterns)) {
                    this.acl.file_patterns = [];
                }
                this.operations = Array.isArray(data.operations)
                    ? data.operations
                    : (Array.isArray((data.server || {}).operations) ? data.server.operations : []);
                this.server = data.server || {};
                if (!this.operations.length && Array.isArray(this.server.operations)) {
                    this.operations = this.server.operations;
                }
                this.retention = data.retention || {};
                this.security = data.security || { trusted_ips: [], blocked_user_agents: [], rate_limits: {}, threat_feed: [], audit: {} };
                if (!this.security || typeof this.security !== 'object') {
                    this.security = {};
                }
                if (!this.security.rate_limits || typeof this.security.rate_limits !== 'object') {
                    this.security.rate_limits = {};
                }
                if (this.security.rate_limits.max_call_concurrency === undefined || this.security.rate_limits.max_call_concurrency === null) {
                    this.security.rate_limits.max_call_concurrency = 0;
                }
                if (this.security.rate_limits.max_registration_concurrency === undefined || this.security.rate_limits.max_registration_concurrency === null) {
                    this.security.rate_limits.max_registration_concurrency = 0;
                }
                this.security.acl_rules = this.formatAclRules(this.acl.active_rules);
                this.asr = data.asr || { providers: [], languages: [], callback: {} };
                this.selectedProvider = this.asr.active_provider || (this.asr.providers?.[0]?.id || '');
                const storageMeta = this.server.storage || {};
                this.selectedStorageProfile = storageMeta.active_profile
                    || storageMeta.mode
                    || (this.server.storage_profiles?.[0]?.id || '');
                if (this.storageProfiles.length) {
                    const exists = this.storageProfiles.find((profile) => profile.id === this.selectedStorageProfile);
                    if (!exists) {
                        this.selectedStorageProfile = this.storageProfiles[0].id;
                    }
                }
                this.directory = { departments: [], users: [], pending_invites: [] };
                this.departmentPagination = null;
                this.userPagination = null;
                this.departmentError = null;
                this.userError = null;
                this.departmentParams.page = 1;
                this.departmentParams.perPage = 20;
                this.departmentParams.q = '';
                this.userParams.page = 1;
                this.userParams.perPage = 20;
                this.userParams.q = '';
                this.userParams.activeOnly = false;
                this.departmentDraft = this.defaultDepartmentDraft();
                this.userDraft = this.defaultUserDraft();
                this.departmentFormMode = 'create';
                this.departmentFormVisible = false;
                this.departmentEditingId = '';
                this.userFormMode = 'create';
                this.userFormVisible = false;
                this.userEditingId = '';
                this.selectedDepartmentId = '';
                this.selectedUserId = '';
                this.fetchDepartments();
                this.fetchUsers();
                this.resetAclReloadModal();
                this.resetReloadModal();
            },
            get storageProfiles() {
                return Array.isArray(this.server.storage_profiles) ? this.server.storage_profiles : [];
            },
            selectStorageProfile(id) {
                this.selectedStorageProfile = id;
            },
            get selectedStorageProfileData() {
                return this.storageProfiles.find((item) => item.id === this.selectedStorageProfile) || null;
            },
            profileConfig(profile) {
                if (!profile || typeof profile.config !== 'object') {
                    return [];
                }
                return Object.entries(profile.config).map(([key, value]) => {
                    let display;
                    if (value === null || value === undefined) {
                        display = '—';
                    } else if (typeof value === 'number') {
                        display = value.toString();
                    } else if (typeof value === 'boolean') {
                        display = value ? 'true' : 'false';
                    } else if (typeof value === 'object') {
                        try {
                            display = JSON.stringify(value, null, 2);
                        } catch (err) {
                            display = String(value);
                        }
                    } else {
                        display = String(value);
                    }
                    return {
                        key,
                        label: this.labelize(key),
                        value: display,
                    };
                });
            },
            labelize(key) {
                return String(key)
                    .replace(/[_-]/g, ' ')
                    .replace(/\b\w/g, (match) => match.toUpperCase());
            },
            storageModeLabel(mode) {
                switch ((mode || '').toLowerCase()) {
                    case 'local':
                        return 'Local only';
                    case 'hybrid':
                        return 'Hybrid';
                    case 'http':
                        return 'HTTP webhook';
                    case 's3':
                    case 'remote':
                        return 'Remote (S3)';
                    default:
                        return mode || 'Unknown';
                }
            },
            get selectedDepartment() {
                return (this.directory.departments || []).find((dept) => String(dept.id) === String(this.selectedDepartmentId)) || null;
            },
            selectDepartment(id) {
                this.selectedDepartmentId = id;
            },
            get departmentSummary() {
                const total = this.departmentPagination?.total_items;
                if (typeof total === 'number') {
                    return `${total} ${total === 1 ? 'department' : 'departments'}`;
                }
                const count = (this.directory.departments || []).length;
                return `${count} ${count === 1 ? 'department' : 'departments'}`;
            },
            get selectedUser() {
                return (this.directory.users || []).find((user) => String(user.id) === String(this.selectedUserId)) || null;
            },
            selectUser(id) {
                this.selectedUserId = id;
            },
            get userSummary() {
                const total = this.userPagination?.total_items;
                if (typeof total === 'number') {
                    return `${total} ${total === 1 ? 'user' : 'users'}`;
                }
                const count = (this.directory.users || []).length;
                return `${count} ${count === 1 ? 'user' : 'users'}`;
            },
            userStatusClasses(user) {
                if (!user) {
                    return 'bg-slate-100 text-slate-600 ring-1 ring-slate-200';
                }
                if (!user.is_active) {
                    return 'bg-amber-50 text-amber-600 ring-1 ring-amber-200';
                }
                if (user.is_superuser) {
                    return 'bg-purple-50 text-purple-600 ring-1 ring-purple-200';
                }
                if (user.is_staff) {
                    return 'bg-sky-50 text-sky-600 ring-1 ring-sky-200';
                }
                return 'bg-emerald-50 text-emerald-600 ring-1 ring-emerald-200';
            },
            userStatusLabel(user) {
                if (!user) {
                    return 'Unknown';
                }
                if (!user.is_active) {
                    return 'Inactive';
                }
                if (user.is_superuser) {
                    return 'Superuser';
                }
                if (user.is_staff) {
                    return 'Staff';
                }
                return 'Active';
            },
            lastLoginLabel(user) {
                if (!user) {
                    return 'Last login · —';
                }
                const label = user.last_login_at ? this.formatDateTime(user.last_login_at) : '—';
                return `Last login · ${label}`;
            },
            get activeProvider() {
                return this.asr.providers?.find((provider) => provider.id === this.selectedProvider) || null;
            },
            formatDateTime(value) {
                if (!value) {
                    return '—';
                }
                try {
                    const date = new Date(value);
                    if (Number.isNaN(date.getTime())) {
                        return value;
                    }
                    return date.toLocaleString();
                } catch (err) {
                    return value;
                }
            },
            capitalize(input) {
                if (!input) {
                    return '';
                }
                return String(input).charAt(0).toUpperCase() + String(input).slice(1);
            },
            formatMetadata(value) {
                if (value === null || value === undefined) {
                    return '—';
                }
                if (typeof value === 'string') {
                    return value;
                }
                try {
                    return JSON.stringify(value, null, 2);
                } catch (err) {
                    return String(value);
                }
            },
            departmentEndpoint() {
                return `${this.basePath}/settings/departments`;
            },
            departmentDetailEndpoint(id) {
                return `${this.departmentEndpoint()}/${id}`;
            },
            buildDepartmentParams() {
                return {
                    page: this.departmentParams.page || 1,
                    per_page: this.departmentParams.perPage || 20,
                    filters: {
                        q: (this.departmentParams.q || '').trim(),
                    },
                };
            },
            applyDepartmentFilters() {
                this.departmentParams.page = 1;
                this.fetchDepartments();
            },
            resetDepartmentFilters() {
                this.departmentParams.q = '';
                this.departmentParams.page = 1;
                this.fetchDepartments();
            },
            async fetchDepartments() {
                this.departmentLoading = true;
                this.departmentError = null;
                try {
                    const response = await fetch(`${this.departmentEndpoint()}`, {
                        method: 'POST',
                        headers: {
                            Accept: 'application/json',
                            'Content-Type': 'application/json',
                        },
                        body: JSON.stringify(this.buildDepartmentParams()),
                    });
                    const data = await response.json().catch(() => ({}));
                    if (!response.ok) {
                        throw new Error(data?.message || 'Failed to load departments');
                    }

                    const items = Array.isArray(data?.items) ? data.items : [];

                    const perPageRaw = Number(data?.per_page);
                    const totalItemsRaw = Number(data?.total_items);
                    const totalPagesRaw = Number(data?.total_pages);
                    const currentPageRaw = Number(data?.page);

                    const fallbackPerPage = this.departmentParams.perPage || 20;
                    const perPage = Number.isFinite(perPageRaw) && perPageRaw > 0 ? perPageRaw : fallbackPerPage;
                    const totalItems = Number.isFinite(totalItemsRaw) && totalItemsRaw >= 0 ? totalItemsRaw : items.length;
                    const inferredTotalPages = Math.max(Math.ceil(totalItems / Math.max(perPage, 1)), 1);
                    const totalPages = Number.isFinite(totalPagesRaw) && totalPagesRaw >= 1
                        ? Math.max(Math.min(totalPagesRaw, inferredTotalPages || 1), 1)
                        : inferredTotalPages;
                    const currentPage = Number.isFinite(currentPageRaw) && currentPageRaw >= 1
                        ? Math.min(currentPageRaw, totalPages)
                        : Math.min(this.departmentParams.page || 1, totalPages);

                    const hasPrevApi = typeof data?.has_prev === 'boolean' ? data.has_prev : null;
                    const hasNextApi = typeof data?.has_next === 'boolean' ? data.has_next : null;
                    const resultsCount = items.length;
                    const showingFrom = resultsCount ? ((currentPage - 1) * perPage) + 1 : 0;
                    const showingTo = resultsCount ? Math.min(showingFrom + resultsCount - 1, totalItems) : 0;
                    const hasPrev = hasPrevApi !== null ? hasPrevApi : currentPage > 1;
                    const hasNext = hasNextApi !== null ? hasNextApi : currentPage < totalPages;
                    const prevPage = hasPrev ? Math.max(currentPage - 1, 1) : null;
                    const nextPage = hasNext ? Math.min(currentPage + 1, totalPages) : null;

                    this.directory.departments = items;
                    this.departmentPagination = {
                        current_page: currentPage,
                        per_page: perPage,
                        total_items: totalItems,
                        total_pages: totalPages,
                        has_prev: hasPrev,
                        has_next: hasNext,
                        prev_page: prevPage,
                        next_page: nextPage,
                        showing_from: showingFrom,
                        showing_to: showingTo,
                    };
                    this.departmentParams.page = currentPage;
                    this.departmentParams.perPage = perPage;
                    if (!items.some((item) => String(item.id) === String(this.selectedDepartmentId))) {
                        this.selectedDepartmentId = items[0]?.id ?? '';
                    }
                } catch (err) {
                    console.error(err);
                    this.departmentError = err.message || 'Failed to load departments';
                    this.directory.departments = [];
                    this.departmentPagination = null;
                    this.selectedDepartmentId = '';
                } finally {
                    this.departmentLoading = false;
                }
            },
            async loadDepartmentDetail(id) {
                if (id === undefined || id === null) {
                    return null;
                }
                try {
                    const response = await fetch(this.departmentDetailEndpoint(id), {
                        method: 'GET',
                        headers: {
                            Accept: 'application/json',
                        },
                    });
                    const data = await response.json().catch(() => ({}));
                    if (!response.ok) {
                        throw new Error(data?.message || 'Failed to load department');
                    }
                    let updated = false;
                    this.directory.departments = (this.directory.departments || []).map((dept) => {
                        if (String(dept.id) === String(id)) {
                            updated = true;
                            return data;
                        }
                        return dept;
                    });
                    if (!updated) {
                        this.directory.departments.push(data);
                    }
                    return data;
                } catch (err) {
                    console.error(err);
                    this.$dispatch('toast', {
                        title: 'Unable to load',
                        message: err.message || 'Failed to load department details.',
                    });
                    return null;
                }
            },
            metadataToInput(value) {
                if (value === null || value === undefined) {
                    return '';
                }
                if (typeof value === 'string') {
                    return value;
                }
                try {
                    return JSON.stringify(value, null, 2);
                } catch (_) {
                    return '';
                }
            },
            parseMetadataInput(input) {
                const raw = (input || '').trim();
                if (!raw.length) {
                    return null;
                }
                try {
                    return JSON.parse(raw);
                } catch (_) {
                    throw new Error('Metadata must be valid JSON.');
                }
            },
            normalizeOptionalString(value) {
                if (value === null || value === undefined) {
                    return null;
                }
                const trimmed = String(value).trim();
                return trimmed.length ? trimmed : null;
            },
            slugify(value) {
                return String(value || '')
                    .toLowerCase()
                    .trim()
                    .replace(/[^a-z0-9]+/g, '-')
                    .replace(/^-+|-+$/g, '')
                    .substring(0, 120);
            },
            defaultDepartmentDraft() {
                return {
                    name: '',
                    display_label: '',
                    slug: '',
                    color: '',
                    manager_contact: '',
                    description: '',
                    metadataInput: '',
                };
            },
            departmentToDraft(dept) {
                return {
                    name: dept?.name || '',
                    display_label: dept?.display_label || '',
                    slug: dept?.slug || '',
                    color: dept?.color || '',
                    manager_contact: dept?.manager_contact || '',
                    description: dept?.description || '',
                    metadataInput: this.metadataToInput(dept?.metadata),
                };
            },
            openDepartmentCreate() {
                this.departmentFormMode = 'create';
                this.departmentEditingId = '';
                this.departmentDraft = this.defaultDepartmentDraft();
                this.departmentFormVisible = true;
            },
            async openDepartmentEdit(id) {
                const detail = await this.loadDepartmentDetail(id);
                if (!detail) {
                    return;
                }
                this.departmentFormMode = 'edit';
                this.departmentEditingId = id;
                this.departmentDraft = this.departmentToDraft(detail);
                this.departmentFormVisible = true;
            },
            cancelDepartmentForm() {
                this.departmentFormVisible = false;
                this.departmentEditingId = '';
                this.departmentFormMode = 'create';
                this.departmentDraft = this.defaultDepartmentDraft();
            },
            async saveDepartment() {
                if (this.departmentProcessing.save) {
                    return;
                }
                const draft = this.departmentDraft || this.defaultDepartmentDraft();
                const name = (draft.name || '').trim();
                if (!name.length) {
                    this.$dispatch('toast', {
                        title: 'Missing information',
                        message: 'Department name is required.',
                    });
                    return;
                }
                let metadata;
                try {
                    metadata = this.parseMetadataInput(draft.metadataInput);
                } catch (err) {
                    this.$dispatch('toast', {
                        title: 'Invalid metadata',
                        message: err.message || 'Metadata must be valid JSON.',
                    });
                    return;
                }
                const body = {
                    name,
                    display_label: this.normalizeOptionalString(draft.display_label),
                    description: this.normalizeOptionalString(draft.description),
                    color: this.normalizeOptionalString(draft.color),
                    manager_contact: this.normalizeOptionalString(draft.manager_contact),
                    metadata,
                };
                const slugInput = (draft.slug || '').trim();
                if (this.departmentFormMode === 'edit') {
                    body.slug = slugInput.length ? slugInput : null;
                } else {
                    body.slug = slugInput.length ? slugInput : this.slugify(name);
                }
                this.departmentProcessing.save = true;
                try {
                    const method = this.departmentFormMode === 'edit' ? 'PATCH' : 'PUT';
                    const endpoint = method === 'PATCH'
                        ? this.departmentDetailEndpoint(this.departmentEditingId)
                        : this.departmentEndpoint();
                    const response = await fetch(endpoint, {
                        method,
                        headers: {
                            Accept: 'application/json',
                            'Content-Type': 'application/json',
                        },
                        body: JSON.stringify(body),
                    });
                    const data = await response.json().catch(() => ({}));
                    if (!response.ok) {
                        throw new Error(data?.message || 'Failed to save department');
                    }
                    if (method === 'PUT' && data?.id !== undefined) {
                        this.selectedDepartmentId = data.id;
                    }
                    this.$dispatch('toast', {
                        title: 'Saved',
                        message: method === 'PUT' ? 'Department created successfully.' : 'Department updated successfully.',
                    });
                    this.cancelDepartmentForm();
                    await this.fetchDepartments();
                } catch (err) {
                    console.error(err);
                    this.$dispatch('toast', {
                        title: 'Save failed',
                        message: err.message || 'Unable to save department.',
                    });
                } finally {
                    this.departmentProcessing.save = false;
                }
            },
            deleteDepartment(id) {
                const dept = (this.directory.departments || []).find((item) => String(item.id) === String(id));
                if (!dept) {
                    return;
                }
                window.dispatchEvent(new CustomEvent('console:confirm', {
                    detail: {
                        title: 'Delete department',
                        message: `Delete ${dept.name}? This action cannot be undone.`,
                        confirmLabel: 'Delete',
                        cancelLabel: 'Cancel',
                        destructive: true,
                        onConfirm: () => this.performDeleteDepartment(id),
                    },
                }));
            },
            async performDeleteDepartment(id) {
                this.departmentProcessing.delete = id;
                this.departmentError = null;
                try {
                    const response = await fetch(this.departmentDetailEndpoint(id), {
                        method: 'DELETE',
                        headers: {
                            Accept: 'application/json',
                        },
                    });
                    const data = await response.json().catch(() => ({}));
                    if (!response.ok) {
                        throw new Error(data?.message || 'Failed to delete department');
                    }
                    this.$dispatch('toast', {
                        title: 'Deleted',
                        message: 'Department removed successfully.',
                    });
                    if (String(this.selectedDepartmentId) === String(id)) {
                        this.selectedDepartmentId = '';
                    }
                    await this.fetchDepartments();
                } catch (err) {
                    console.error(err);
                    this.departmentError = err.message || 'Failed to delete department';
                    this.$dispatch('toast', {
                        title: 'Delete failed',
                        message: this.departmentError,
                    });
                } finally {
                    this.departmentProcessing.delete = null;
                }
            },
            userEndpoint() {
                return `${this.basePath}/settings/users`;
            },
            userDetailEndpoint(id) {
                return `${this.userEndpoint()}/${id}`;
            },
            buildUserParams() {
                return {
                    filters: {
                        q: (this.userParams.q || '').trim(),
                        active: this.userParams.activeOnly ? true : undefined,
                    },
                    page: this.userParams.page || 1,
                    per_page: this.userParams.perPage || 20,
                };
            },
            applyUserFilters() {
                this.userParams.page = 1;
                this.fetchUsers();
            },
            resetUserFilters() {
                this.userParams.q = '';
                this.userParams.activeOnly = false;
                this.userParams.page = 1;
                this.fetchUsers();
            },
            async fetchUsers() {
                this.userLoading = true;
                this.userError = null;
                try {
                    const response = await fetch(`${this.userEndpoint()}`, {
                        method: 'POST',
                        headers: {
                            Accept: 'application/json',
                            'Content-Type': 'application/json',
                        },
                        body: JSON.stringify(this.buildUserParams()),
                    });
                    const data = await response.json().catch(() => ({}));
                    if (!response.ok) {
                        throw new Error(data?.message || 'Failed to load users');
                    }

                    const items = Array.isArray(data?.items) ? data.items : [];

                    const perPageRaw = Number(data?.per_page);
                    const totalItemsRaw = Number(data?.total_items);
                    const totalPagesRaw = Number(data?.total_pages);
                    const currentPageRaw = Number(data?.page);

                    const fallbackPerPage = this.userParams.perPage || 20;
                    const perPage = Number.isFinite(perPageRaw) && perPageRaw > 0 ? perPageRaw : fallbackPerPage;
                    const totalItems = Number.isFinite(totalItemsRaw) && totalItemsRaw >= 0 ? totalItemsRaw : items.length;
                    const inferredTotalPages = Math.max(Math.ceil(totalItems / Math.max(perPage, 1)), 1);
                    const totalPages = Number.isFinite(totalPagesRaw) && totalPagesRaw >= 1
                        ? Math.max(Math.min(totalPagesRaw, inferredTotalPages || 1), 1)
                        : inferredTotalPages;
                    const currentPage = Number.isFinite(currentPageRaw) && currentPageRaw >= 1
                        ? Math.min(currentPageRaw, totalPages)
                        : Math.min(this.userParams.page || 1, totalPages);

                    const hasPrevApi = typeof data?.has_prev === 'boolean' ? data.has_prev : null;
                    const hasNextApi = typeof data?.has_next === 'boolean' ? data.has_next : null;
                    const resultsCount = items.length;
                    const showingFrom = resultsCount ? ((currentPage - 1) * perPage) + 1 : 0;
                    const showingTo = resultsCount ? Math.min(showingFrom + resultsCount - 1, totalItems) : 0;
                    const hasPrev = hasPrevApi !== null ? hasPrevApi : currentPage > 1;
                    const hasNext = hasNextApi !== null ? hasNextApi : currentPage < totalPages;
                    const prevPage = hasPrev ? Math.max(currentPage - 1, 1) : null;
                    const nextPage = hasNext ? Math.min(currentPage + 1, totalPages) : null;

                    this.directory.users = items;
                    this.userPagination = {
                        current_page: currentPage,
                        per_page: perPage,
                        total_items: totalItems,
                        total_pages: totalPages,
                        has_prev: hasPrev,
                        has_next: hasNext,
                        prev_page: prevPage,
                        next_page: nextPage,
                        showing_from: showingFrom,
                        showing_to: showingTo,
                    };
                    this.userParams.page = currentPage;
                    this.userParams.perPage = perPage;
                    if (!items.some((item) => String(item.id) === String(this.selectedUserId))) {
                        this.selectedUserId = items[0]?.id ?? '';
                    }
                } catch (err) {
                    console.error(err);
                    this.userError = err.message || 'Failed to load users';
                    this.directory.users = [];
                    this.userPagination = null;
                    this.selectedUserId = '';
                } finally {
                    this.userLoading = false;
                }
            },
            async loadUserDetail(id) {
                if (id === undefined || id === null) {
                    return null;
                }
                try {
                    const response = await fetch(this.userDetailEndpoint(id), {
                        method: 'GET',
                        headers: {
                            Accept: 'application/json',
                        },
                    });
                    const data = await response.json().catch(() => ({}));
                    if (!response.ok) {
                        throw new Error(data?.message || 'Failed to load user');
                    }
                    let updated = false;
                    this.directory.users = (this.directory.users || []).map((user) => {
                        if (String(user.id) === String(id)) {
                            updated = true;
                            return data;
                        }
                        return user;
                    });
                    if (!updated) {
                        this.directory.users.push(data);
                    }
                    return data;
                } catch (err) {
                    console.error(err);
                    this.$dispatch('toast', {
                        title: 'Unable to load',
                        message: err.message || 'Failed to load user details.',
                    });
                    return null;
                }
            },
            defaultUserDraft() {
                return {
                    email: '',
                    username: '',
                    password: '',
                    is_active: true,
                    is_staff: false,
                    is_superuser: false,
                };
            },
            userToDraft(user) {
                return {
                    email: user?.email || '',
                    username: user?.username || '',
                    password: '',
                    is_active: !!user?.is_active,
                    is_staff: !!user?.is_staff,
                    is_superuser: !!user?.is_superuser,
                };
            },
            openUserCreate() {
                this.userFormMode = 'create';
                this.userEditingId = '';
                this.userDraft = this.defaultUserDraft();
                this.userFormVisible = true;
            },
            async openUserEdit(id) {
                const detail = await this.loadUserDetail(id);
                if (!detail) {
                    return;
                }
                this.userFormMode = 'edit';
                this.userEditingId = id;
                this.userDraft = this.userToDraft(detail);
                this.userFormVisible = true;
            },
            cancelUserForm() {
                this.userFormVisible = false;
                this.userEditingId = '';
                this.userFormMode = 'create';
                this.userDraft = this.defaultUserDraft();
            },
            async saveUser() {
                if (this.userProcessing.save) {
                    return;
                }
                const draft = this.userDraft || this.defaultUserDraft();
                const email = (draft.email || '').trim();
                const username = (draft.username || '').trim();
                if (!email.length || !username.length) {
                    this.$dispatch('toast', {
                        title: 'Missing information',
                        message: 'Email and username are required.',
                    });
                    return;
                }
                const body = {
                    email,
                    username,
                    is_active: !!draft.is_active,
                    is_staff: !!draft.is_staff,
                    is_superuser: !!draft.is_superuser,
                };
                const password = (draft.password || '').trim();
                if (this.userFormMode === 'edit') {
                    if (password.length) {
                        body.password = password;
                    }
                } else if (!password.length) {
                    this.$dispatch('toast', {
                        title: 'Missing password',
                        message: 'Set an initial password for new users.',
                    });
                    return;
                } else {
                    body.password = password;
                }
                this.userProcessing.save = true;
                try {
                    const method = this.userFormMode === 'edit' ? 'PATCH' : 'PUT';
                    const endpoint = method === 'PATCH'
                        ? this.userDetailEndpoint(this.userEditingId)
                        : this.userEndpoint();
                    const response = await fetch(endpoint, {
                        method,
                        headers: {
                            Accept: 'application/json',
                            'Content-Type': 'application/json',
                        },
                        body: JSON.stringify(body),
                    });
                    const data = await response.json().catch(() => ({}));
                    if (!response.ok) {
                        throw new Error(data?.message || 'Failed to save user');
                    }
                    if (method === 'PUT' && data?.id !== undefined) {
                        this.selectedUserId = data.id;
                    }
                    this.$dispatch('toast', {
                        title: 'Saved',
                        message: method === 'PUT' ? 'User created successfully.' : 'User updated successfully.',
                    });
                    this.cancelUserForm();
                    await this.fetchUsers();
                } catch (err) {
                    console.error(err);
                    this.$dispatch('toast', {
                        title: 'Save failed',
                        message: err.message || 'Unable to save user.',
                    });
                } finally {
                    this.userProcessing.save = false;
                }
            },
            deleteUser(id) {
                const user = (this.directory.users || []).find((item) => String(item.id) === String(id));
                if (!user) {
                    return;
                }
                window.dispatchEvent(new CustomEvent('console:confirm', {
                    detail: {
                        title: 'Delete user',
                        message: `Delete ${user.username || user.email}? This action cannot be undone.`,
                        confirmLabel: 'Delete',
                        cancelLabel: 'Cancel',
                        destructive: true,
                        onConfirm: () => this.performDeleteUser(id),
                    },
                }));
            },
            async performDeleteUser(id) {
                this.userProcessing.delete = id;
                this.userError = null;
                try {
                    const response = await fetch(this.userDetailEndpoint(id), {
                        method: 'DELETE',
                        headers: {
                            Accept: 'application/json',
                        },
                    });
                    const data = await response.json().catch(() => ({}));
                    if (!response.ok) {
                        throw new Error(data?.message || 'Failed to delete user');
                    }
                    this.$dispatch('toast', {
                        title: 'Deleted',
                        message: 'User removed successfully.',
                    });
                    if (String(this.selectedUserId) === String(id)) {
                        this.selectedUserId = '';
                    }
                    await this.fetchUsers();
                } catch (err) {
                    console.error(err);
                    this.userError = err.message || 'Failed to delete user';
                    this.$dispatch('toast', {
                        title: 'Delete failed',
                        message: this.userError,
                    });
                } finally {
                    this.userProcessing.delete = null;
                }
            },
            generateId(prefix = 'id') {
                if (typeof window !== 'undefined' && window.crypto && typeof window.crypto.randomUUID === 'function') {
                    return `${prefix}-${window.crypto.randomUUID()}`;
                }
                return `${prefix}-${Date.now().toString(36)}-${Math.random().toString(16).slice(2, 8)}`;
            },
            operationDisabled(action) {
                if (!action) {
                    return true;
                }
                if (action.id === 'reload-acl') {
                    return !this.acl.reload_supported || this.aclReloading || this.aclReloadModal.open;
                }
                if (action.id === 'reload-app') {
                    return this.appReloading || this.appReloadModal.open;
                }
                return false;
            },
            operationButtonLabel(action) {
                if (!action) {
                    return 'Run action';
                }
                if (action.id === 'reload-acl') {
                    if (!this.acl.reload_supported) {
                        return 'Proxy offline';
                    }
                    if (this.aclReloading) {
                        return 'Reloading...';
                    }
                    if (this.aclReloadModal.open) {
                        return 'Confirming...';
                    }
                    return 'Run action';
                }
                if (action.id === 'reload-app') {
                    return this.appReloading ? 'Reloading...' : 'Run action';
                }
                return 'Run action';
            },
            async triggerOperation(action) {
                if (!action) {
                    return;
                }
                if (action.id === 'reload-acl') {
                    this.showAclReloadModal(action);
                    return;
                }
                if (action.id === 'reload-app') {
                    this.showReloadModal(action);
                    return;
                }
                const timestamp = new Date().toLocaleTimeString();
                this.lastOperation = {
                    label: action.label,
                    timestamp,
                    status: 'Operation acknowledged (no handler configured).',
                };
                this.$dispatch('toast', {
                    title: 'Operation queued',
                    message: `${action.label || 'Operation'} acknowledged.`,
                });
            },
            showAclReloadModal(action) {
                this.resetAclReloadModal();
                this.aclReloadModal.open = true;
                this.aclReloadModal.action = action;
            },
            closeAclReloadModal() {
                if (this.aclReloading) {
                    return;
                }
                this.aclReloadModal.open = false;
            },
            resetAclReloadModal() {
                this.aclReloadModal = {
                    open: false,
                    status: 'idle',
                    error: null,
                    message: '',
                    action: null,
                };
            },
            get aclReloadStatusLabel() {
                switch (this.aclReloadModal.status) {
                    case 'running':
                        return 'Reload in progress…';
                    case 'success':
                        return 'Rules refreshed successfully';
                    case 'error':
                        return 'Reload request failed';
                    default:
                        return 'Awaiting confirmation';
                }
            },
            async startAclReload() {
                if (this.aclReloading || this.aclReloadModal.status === 'running') {
                    return;
                }
                this.aclReloadModal.status = 'running';
                this.aclReloadModal.error = null;
                this.aclReloadModal.message = '';
                await this.reloadAcl();
            },
            async reloadAcl() {
                if (!this.acl.reload_supported) {
                    this.$dispatch('toast', {
                        title: 'Not available',
                        message: 'ACL reload is only available while the SIP proxy is running.',
                    });
                    if (this.aclReloadModal.open) {
                        this.aclReloadModal.status = 'error';
                        this.aclReloadModal.error = 'SIP proxy is offline. Reload unavailable.';
                    }
                    return;
                }
                if (this.aclReloading) {
                    return;
                }
                this.aclReloading = true;
                try {
                    if (this.aclReloadModal.open && this.aclReloadModal.status !== 'running') {
                        this.aclReloadModal.status = 'running';
                        this.aclReloadModal.error = null;
                        this.aclReloadModal.message = '';
                    }
                    const endpoint = `${this.amiEndpoint.replace(/\/$/, '')}/reload/acl`;
                    const response = await fetch(endpoint, {
                        method: 'POST',
                        headers: {
                            Accept: 'application/json',
                        },
                        credentials: 'include',
                    });
                    const data = await response.json().catch(() => ({}));
                    if (!response.ok) {
                        throw new Error(data?.message || 'Failed to reload ACL rules');
                    }
                    if (Array.isArray(data?.active_rules)) {
                        this.acl.active_rules = data.active_rules;
                    }
                    if (data?.metrics) {
                        this.acl.metrics = data.metrics;
                    }
                    this.security.acl_rules = this.formatAclRules(this.acl.active_rules);
                    const total = Number.isFinite(Number(data?.acl_rules_reloaded))
                        ? Number(data.acl_rules_reloaded)
                        : (data?.metrics?.total ?? this.acl.active_rules.length);
                    const durationText = this.formatDuration(data?.metrics?.duration_ms);
                    const message = durationText
                        ? `Loaded ${total} rule${total === 1 ? '' : 's'} in ${durationText}.`
                        : `Loaded ${total} rule${total === 1 ? '' : 's'}.`;
                    this.$dispatch('toast', {
                        title: 'ACL reloaded',
                        message,
                    });
                    if (this.aclReloadModal.open) {
                        this.aclReloadModal.status = 'success';
                        this.aclReloadModal.error = null;
                        this.aclReloadModal.message = message;
                    }
                    this.lastOperation = {
                        label: 'Reload ACL rules',
                        timestamp: new Date().toLocaleString(),
                        status: message,
                    };
                } catch (err) {
                    if (this.aclReloadModal.open) {
                        this.aclReloadModal.status = 'error';
                        this.aclReloadModal.error = err?.message || 'Unable to reload ACL rules.';
                        this.aclReloadModal.message = '';
                    }
                    this.$dispatch('toast', {
                        title: 'Reload failed',
                        message: err?.message || 'Unable to reload ACL rules.',
                    });
                } finally {
                    this.aclReloading = false;
                }
            },
            showReloadModal(action) {
                this.resetReloadModal();
                this.appReloadModal.open = true;
                this.appReloadModal.action = action;
                this.appReloadModal.mode = 'reload';
                this.appReloadModal.checks = this.buildReloadChecklist();
            },
            closeReloadModal() {
                if (this.appReloading) {
                    return;
                }
                this.appReloadModal.open = false;
            },
            resetReloadModal() {
                this.appReloadModal = {
                    open: false,
                    status: 'idle',
                    error: null,
                    message: '',
                    action: null,
                    mode: 'reload',
                    checks: [],
                };
            },
            buildReloadChecklist() {
                return [
                    {
                        id: 'config-parse',
                        label: 'Load configuration file',
                        hint: 'Ensure the TOML file is parseable and present.',
                        status: 'pending',
                    },
                    {
                        id: 'console-enabled',
                        label: 'Console availability',
                        hint: 'Console must stay enabled to manage reloads.',
                        status: 'pending',
                    },
                    {
                        id: 'ports-available',
                        label: 'Port availability',
                        hint: 'New bindings must be free before restart.',
                        status: 'pending',
                    },
                    {
                        id: 'restart-services',
                        label: 'Restart services',
                        hint: 'Stop running services and relaunch with new config.',
                        status: 'pending',
                    },
                ];
            },
            reloadCheckIndicatorClass(status) {
                switch (status) {
                    case 'success':
                        return 'bg-emerald-500';
                    case 'running':
                        return 'bg-sky-400 animate-pulse';
                    case 'error':
                        return 'bg-rose-500';
                    default:
                        return 'bg-slate-300';
                }
            },
            reloadCheckStatusClass(status) {
                switch (status) {
                    case 'success':
                        return 'text-emerald-600';
                    case 'running':
                        return 'text-sky-600';
                    case 'error':
                        return 'text-rose-600';
                    default:
                        return 'text-slate-400';
                }
            },
            reloadCheckStatusLabel(status) {
                switch (status) {
                    case 'success':
                        return 'Done';
                    case 'running':
                        return 'Running';
                    case 'error':
                        return 'Failed';
                    default:
                        return 'Pending';
                }
            },
            get reloadStatusLabel() {
                const mode = this.appReloadModal.mode || 'reload';
                switch (this.appReloadModal.status) {
                    case 'running':
                        return mode === 'check' ? 'Validating configuration…' : 'Running preflight checks…';
                    case 'success':
                        return mode === 'check'
                            ? 'Configuration valid. Ready to reload.'
                            : 'Reload succeeded. Services restarting…';
                    case 'error':
                        return mode === 'check'
                            ? 'Validation failed. Review configuration.'
                            : 'Reload request failed.';
                    default:
                        return 'Awaiting confirmation';
                }
            },
            get reloadConfirmLabel() {
                if (this.appReloadModal.mode === 'check') {
                    if (this.appReloadModal.status === 'success') {
                        return 'Reload now';
                    }
                    return 'Confirm reload';
                }
                return this.appReloadModal.status === 'error' ? 'Retry reload' : 'Confirm reload';
            },
            markCheck(id, status, detail = null) {
                this.appReloadModal.checks = this.appReloadModal.checks.map((item) =>
                    item.id === id ? { ...item, status, detail: detail || item.detail } : item
                );
            },
            async startReloadApplication() {
                if (this.appReloading || this.appReloadModal.status === 'running') {
                    return;
                }
                await this.performReload({ checkOnly: false });
            },
            async startReloadCheck() {
                if (this.appReloading || this.appReloadModal.status === 'running') {
                    return;
                }
                await this.performReload({ checkOnly: true });
            },
            async performReload({ checkOnly = false } = {}) {
                const action = this.appReloadModal.action || {};
                const baseEndpoint = action.endpoint || `${this.amiEndpoint.replace(/\/$/, '')}/reload/app`;
                const method = action.method || 'POST';
                let endpoint = baseEndpoint;
                if (checkOnly) {
                    const separator = endpoint.includes('?') ? '&' : '?';
                    endpoint = `${endpoint}${separator}mode=check`;
                }

                this.appReloading = true;
                this.appReloadModal.mode = checkOnly ? 'check' : 'reload';
                this.appReloadModal.status = 'running';
                this.appReloadModal.error = null;
                this.appReloadModal.message = checkOnly ? 'Validating configuration…' : '';
                this.markCheck('config-parse', 'running');
                this.markCheck('console-enabled', 'pending');
                this.markCheck('ports-available', 'pending');
                this.markCheck('restart-services', 'pending');

                try {
                    const response = await fetch(endpoint, {
                        method,
                        headers: {
                            Accept: 'application/json',
                        },
                        credentials: 'include',
                    });
                    const data = await response.json().catch(() => ({}));

                    if (!response.ok) {
                        const message = this.handleReloadErrors(data);
                        throw new Error(message);
                    }

                    this.markCheck('config-parse', 'success');
                    this.markCheck('console-enabled', 'success');
                    this.markCheck('ports-available', 'success');

                    if (checkOnly) {
                        this.markCheck('restart-services', 'success', 'Ready to restart once you confirm reload.');
                        const message = data?.message || 'Configuration validated. Services not restarted.';
                        this.appReloadModal.status = 'success';
                        this.appReloadModal.message = message;
                        this.$dispatch('toast', {
                            title: 'Configuration valid',
                            message,
                        });
                        this.lastOperation = {
                            label: 'Check',
                            timestamp: new Date().toLocaleString(),
                            status: message,
                        };
                        return;
                    }

                    this.markCheck('restart-services', 'running');
                    this.markCheck('restart-services', 'success');
                    const message = data?.message || 'Configuration validated. Services will restart automatically.';
                    this.appReloadModal.status = 'success';
                    this.appReloadModal.message = message;
                    this.$dispatch('toast', {
                        title: 'Reloading',
                        message,
                    });
                    this.lastOperation = {
                        label: action.label || 'Reload application',
                        timestamp: new Date().toLocaleString(),
                        status: 'Restart scheduled. Waiting for services to return…',
                    };
                    window.setTimeout(() => {
                        window.location.reload();
                    }, 2500);
                } catch (err) {
                    const fallback = checkOnly ? 'Unable to validate configuration.' : 'Unable to reload application.';
                    const message = err?.message || fallback;
                    this.appReloadModal.status = 'error';
                    this.appReloadModal.error = message;
                    this.appReloadModal.message = '';
                    this.$dispatch('toast', {
                        title: checkOnly ? 'Validation failed' : 'Reload failed',
                        message,
                    });
                    this.lastOperation = {
                        label: checkOnly ? 'Check' : (action.label || 'Reload'),
                        timestamp: new Date().toLocaleString(),
                        status: message,
                    };
                } finally {
                    this.appReloading = false;
                }
            },
            handleReloadErrors(payload) {
                const errors = Array.isArray(payload?.errors) ? payload.errors : [];
                let message = this.formatOperationError(payload);
                if (!errors.length) {
                    this.markCheck('config-parse', 'error', message);
                    return message;
                }

                const lowered = (value) => (value || '').toString().toLowerCase();
                let consoleSet = false;
                let portsSet = false;
                let configSet = false;
                for (const issue of errors) {
                    const field = lowered(issue.field);
                    const detail = issue.message || message;
                    if (field.includes('console')) {
                        this.markCheck('console-enabled', 'error', detail);
                        consoleSet = true;
                    } else if (field.includes('port') || field.includes('addr') || field.includes('http')) {
                        this.markCheck('ports-available', 'error', detail);
                        portsSet = true;
                    } else if (field.includes('config')) {
                        this.markCheck('config-parse', 'error', detail);
                        configSet = true;
                    }
                }

                if (!configSet) {
                    this.markCheck('config-parse', 'success');
                }
                if (!consoleSet) {
                    this.markCheck('console-enabled', 'success');
                }
                if (!portsSet) {
                    this.markCheck('ports-available', 'success');
                }

                this.markCheck('restart-services', 'error', message);
                return message;
            },
            formatAclRules(rules) {
                if (!Array.isArray(rules) || !rules.length) {
                    return 'allow all';
                }
                return rules.join('\n');
            },
            formatOperationError(payload) {
                if (!payload || typeof payload !== 'object') {
                    return 'Request failed.';
                }
                if (Array.isArray(payload.errors) && payload.errors.length) {
                    return payload.errors
                        .map((item) => {
                            if (!item || typeof item !== 'object') {
                                return String(item);
                            }
                            const field = item.field ? `${item.field}: ` : '';
                            return `${field}${item.message || 'Invalid configuration'}`;
                        })
                        .join('; ');
                }
                return payload.message || payload.error || 'Request failed.';
            },
            keyConfigRows() {
                const items = Array.isArray(this.configMeta?.key_items) ? this.configMeta.key_items : [];
                return items.map((item) => {
                    const label = item?.label || '—';
                    const rawValue = item?.value;
                    const value = rawValue === undefined || rawValue === null
                        ? '—'
                        : (typeof rawValue === 'string' ? rawValue : JSON.stringify(rawValue));
                    const hint = item?.hint || null;
                    return { label, value, hint };
                });
            },
            displayMetric(value) {
                return value === undefined || value === null ? '—' : value;
            },
            formatDuration(milliseconds) {
                const value = Number(milliseconds);
                if (!Number.isFinite(value) || value < 0) {
                    return null;
                }
                if (value >= 1000) {
                    const seconds = value / 1000;
                    if (seconds >= 10) {
                        return `${Math.round(seconds)}s`;
                    }
                    return `${seconds.toFixed(1)}s`;
                }
                return `${Math.round(value)}ms`;
            },
            runAsrTest() {
                this.asrProbe = {
                    running: true,
                    success: false,
                    message: 'Contacting provider…',
                    detail: '',
                    timestamp: new Date().toLocaleTimeString(),
                };
                window.setTimeout(() => {
                    const success = Math.random() > 0.1;
                    this.asrProbe = {
                        running: false,
                        success,
                        message: success ? 'ASR connectivity ok' : 'ASR connectivity failed',
                        detail: success ? 'Latency 420 ms · cost estimate $0.0004 / minute' : 'HTTP 401 · token expired (simulated)',
                        timestamp: new Date().toLocaleTimeString(),
                    };
                }, 900);
            },
        }));
    });
</script>
{% endblock %}